C文件是如何变成可执行文件的

  • 预处理:处理所有以#开头的代码(头文件展开,宏定义替换,条件编译)
  • 编译:语法检查,把C文件翻译成汇编文件
  • 汇编:把汇编代码翻译成二进制文件
  • 链接:把所有需要的.o文件合并成一个二进制文件,链接需要用的库。

Pointer code 10 files

01 指针的基本概念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
1. 在64位操作系统中,指针占8个字节
2. 定义的时候(前面有类型),表示后面的变量是指针
3. 使用的时候:表示取值(取指针指向的内存空间的值)
4. int * 和 char * 的步长不一样
*/
#include <stdio.h>

int main(int argc, char const *argv[])
{
int num = 1;
char ch = 'a';


int *p = &num;
char *q = &ch;

printf("%p, %p\n", p, p + 1); //p指向整数,所以p+1加四个字节
printf("%p, %p\n", q, q + 1); //q指向字符,所以q+1加一个字节
return 0;
}

02 指针作为函数参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
1. 如果要交换实参的值,必须传递指针
*/
#include <stdio.h>
void swap(int *x, int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
int main(int argc, char const *argv[])
{
int a = 1, b = 2;
printf("a = %d, b = %d\n", a,b);
swap(&a,&b);
printf("a = %d, b = %d\n", a,b);
return 0;
}

03 指针运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
1. *px++ 与 (*px)++的区别:指针++与值++
*/
#include <stdio.h>

void mystrcpy(char *dest, const char *src)
{
while((*dest++ = *src++) != '\0');
}

int main(int argc, char const *argv[])
{
char s1[32] = "hello";
char s2[32] = "123456789";
mystrcpy(s2,s1);
printf("%s\n", s2);
return 0;
}

void f()
{
int num;
const int *p1 = &num; //const 修饰*p1 即num
// (*p1)++; //error!
p1++;
int *const p2 = &num; //const 修饰p2
// p2++ //error!
(*p2)++;

// const int *const p3 = &num;
}

04 空指针和野指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
1. *px++ 与 (*px)++的区别:指针++与值++
*/
#include <stdio.h>

void mystrcpy(char *dest, const char *src)
{
while((*dest++ = *src++) != '\0');
}

int main(int argc, char const *argv[])
{
char s1[32] = "hello";
char s2[32] = "123456789";
mystrcpy(s2,s1);
printf("%s\n", s2);
return 0;
}

void f()
{
int num;
const int *p1 = &num; //const 修饰*p1 即num
// (*p1)++; //error!
p1++;
int *const p2 = &num; //const 修饰p2
// p2++ //error!
(*p2)++;

// const int *const p3 = &num;
}

05 指针和数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
1. 通过指针的形式访问数组:a[i] <=> *(a+i)
2. 从使用角度说,指针和数组没有区别
*/

#include <stdio.h>

int main(int argc, char const *argv[])
{
char *str = "helloworld";
printf("%c\n", *str);

for (int i = 0; i < 10; ++i)
{
printf("%c", str[i]);
}
return 0;
}

06 指针和数组的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdio.h>

void f(int a[])
{
// 数组名作为参数传递时,变成指针
printf("%d\n", sizeof(a) / sizeof(a[0]));
}

int main(int argc, char const *argv[])
{
char str[32] = "helloworld";
char *p = "helloworld";

p++;
// str++; // error! str不能修改:数组名是常指针


str[0] = 'x';
// p[0] = 'x'; // error! 字符串常量,不能修改

printf("%lu\n", sizeof(str));
printf("%lu\n", sizeof(p));

// 数组作为参数
int a[10] = {0};
printf("%d\n", sizeof(a) / sizeof(a[0]));
f(a);

// &a 表示数组的地址 &a + 1表示下一个数组的地址
// a 表示数组首元素的地址 a + 1表示下一个元素的地址
printf("%p %p\n", &a ,&a + 1);
printf("%p %p\n", a , a + 1);


// 当指针遇上数组:指针数组
char *pstr[] = {"I love China!","I am"};
printf("%s\n", pstr);
printf("%s %s\n", pstr[0],pstr[1]);

return 0;
}

07 指针和函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/*
1. 函数名就是该函数所占内存区的首地址
2. 不能返回局部变量的地址
3. 指针函数 int *p() 与函数指针 int(*p)() 的区别
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void f1()
{
printf("helloworld\n");
}

int add(int a, int b)
{
return a + b;
}

char *init()
{
// char str[32] = {0}; // 不能返回局部变量的地址
char *str = (char *)malloc(128);// 使用堆内存,需要手动释放
return str;
}

typedef int (*T)(int,int); // 声明一个新的类型T,表示函数指针类型

int main(int argc, char const *argv[])
{
void (*p)(); // 定义函数指针 右左法则
p = f1;
p(); // 通过函数指针调用函数,等价于 f1()
// p = add; // error! 类型不兼容
int (*q)(int,int) = add;
printf("%d\n", q(1,2));

T q1 = add; // 等价于 int (*q1)(int, int) = add;


// 指针函数
char *p1 = init();
strcpy(p1,"helloworld");
free(p1);
return 0;
}

08 回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>

int less(int a, int b)
{
return (a > b) ? 1 : 0;
}
int greater(int a, int b)
{
return (a < b) ? 1 : 0;
}
// 回调函数:把函数名作为另外一个函数的参数
// 作用:修改函数的功能
void sort(int *a, int len, int (*p)(int, int))
{
for (int i = 0; i < len - 1; ++i)
{
for (int j = 0; j < len - 1 - i; ++j)
{
// if (a[j] < a[j + 1])
if (p(a[j], a[j + 1])) //通过函数指针调用函数
{
int num = a[j];
a[j] = a[j + 1];
a[j + 1] = num;
}
}
}
}

int main(int argc, char const *argv[])
{
int a[10] = {0};
for (int i = 0; i < 10; ++i)
{
scanf("%d",&a[i]);
}

sort(a, 10, greater);

for (int i = 0; i < 10; ++i)
{
printf("%d ", a[i]);
}
return 0;
}

09 数组指针和指针数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/*
1. 二维数组:int a[3][4]
2. a[0]表示首行首元素的地址 4字节
3. a表示数组首行的地址 16字节
4. &a表示数组的地址 48字节
5. 一维数组对应一级地址:元素的地址
6. 二维数组对应二级地址:行地址
7. 三级地址:数组地址
*/

#include <stdio.h>

int main(int argc, char const *argv[])
{
int a[3][4] = {{1,2,3,4},{2,3,4,5},{3,4,5,6}};

// 用数组指针表示二维数组
int (*p)[4] = a; // 指向一维数组

for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 4; ++j)
{
// printf("%d", p[i][j]);
printf("%d", *(*(p + i) + j)); // a[i] <=> *(a+i)
}
printf("\n");
}


// 用指针数组表示二维数组 右左法则
int *q[3] = {a[0], a[1], a[2]};
for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
{
for (int j = 0; j < sizeof(a[0]) / sizeof(a[0][0]); ++j)
{
printf("%d", q[i][j]);
}
printf("\n");
}

// 用指针表示二维数组
int *p1 = a[0];
for (int i = 0; i < 12; ++i)
{
printf("%d", p1[i]);
}
printf("\n");
return 0;
}

10 指针的指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void init(char **s)
{
*s = (char *)malloc(sizeof(char) * 128);
}
int main(int argc, char const *argv[])
{
char *str = NULL;
init(&str); // 如果要修改实参的值,必须传地址
strcpy(str,"hello");
char *strtemp = str;
while(*str != '\0')
{
printf("%c", *str++);
}
printf("\n");
free(strtemp);
return 0;
}

命令行参数

  • ./program arg1 arg2 arg3
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main(int argc, char const *argv[])
{
for (int i = 0; i < argc; ++i)
{
printf("%s\n", argv[i]);
}
return 0;
}