avatar

目录
01_C语言提高(1)

参考:itheima

[TOC]

1 内存四区

1.1 数据类型本质分析

1.1.1 数据类型的本质

Code
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
#include <stdio.h>
int main(void)
{
int a; //告诉编译器,分配 4个字节
int b[10]; //告诉编译器,分配 4 * 10 个字节

/*类型的本质:固定内存块大小别名
可以通过 sizeof()测试
*/

printf("sizeof(a) = %d, sizeof(b) = %d\n", sizeof(a), sizeof(b));


//打印地址
//数组名字,数组首元素地址,数组首地址
printf("b:%ld, &b:%ld\n", b, &b);


//b, &b的数组类型不一样
//b, 数组首元素地址, 一个元素4字节,+1, +4
//&b, 整个数组的首地址,一个数组4*10 = 40字节,+1, +40
printf("b+1:%ld, &b+1:%ld\n", b+1, &b+1);

//指针类型长度,32位程序, 长度4
// 64位程序, 长度8
char ***********************p = NULL;
int *q = NULL;
printf("%d, %d\n", sizeof(p), sizeof(q));


printf("\n");
return 0;
}
/*
sizeof(a) = 4, sizeof(b) = 40
b:140732775582288, &b:140732775582288
b+1:140732775582292, &b+1:140732775582328
8, 8
*/

1.1.2 数据类型的别名

Code
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
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned int u32;

//typedef和结构体结合使用
struct MyStruct
{
int a;
int b;
};

typedef struct MyStruct2
{
int a;
int b;
}TMP;

/* void, 无类型
1、函数参数为空,定义函数时,可以用void修饰: int fun(void)
2、函数没有返回值:void fun(void);
3、不能定义void类型的普通变量: void a; //err,无法确定类型,不同类型分配空间不一样
4、可以定义void *变量: void *p; //ok, 32永远4字节,64永远8字节
5、数据类型本质:固定内存块大小别名
6、void *p万能指针,函数返回值,函数参数

*/

int main(void)
{
u32 t; // unsigned int

//定义结构体变量,一定要加上struct关键字
struct MyStruct m1;
//MyStruct m2; //err

TMP m3;
struct MyStruct2 m4;

char buf[1024];
strcpy(buf, "1111111111");


printf("\n");
system("pause");
return 0;
}

1.2 变量的本质分析

Code
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
#include <stdio.h>

int main(void)
{
//变量本质:一段连续内存空间别名
//变量相当于门牌号,内存相当于房间
int a;
int *p;

//直接赋值
a = 10;

printf("a = %d\n", a);


//间接赋值
printf("&a: %ld\n", &a);
p = &a;
printf("p = %ld\n", p);

*p = 22;
printf("*p = %d, a = %d\n", *p, a);

printf("\n");
return 0;
}
/*
a = 10
&a: 140732754213496
p = 140732754213496
*p = 22, a = 22
*/

1.3 程序的内存四区模型

Code
1
2
3
4
5
6
7
1)栈区:系统分配空间,系统自动回收,函数内部定义的变量,函数参数,函数结束,其内部变量生命周期结束

2)堆区:程序员动态分配空间,由程序员手动释放,没有手动释放,分配的空间一直可用

3)全局区(全局变量和静态变量,里面又分为初始化区和未初始化区,文字常量区:字符常量):整个程序运行完毕,系统自动回收

4)代码区

全局区分析

全局区:全局变量 静态变量 文字常量区

Code
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

#include <stdio.h>

char *get_str1()
{
char *p = "abcdef"; //文字常量区
//char *p = "abcdef1";
return p;
}

char *get_str2()
{
char *q = "abcdef"; //文字常量区,和上面的常量"abcdef"地址是一样的
//char *q = "abcdef2";
return q;
}

int main(void)
{
char *p = NULL;
char *q = NULL;

p = get_str1();
//%s: 指针指向内存区域的内容
//%d: 打印p本身的值
printf("p = %s, p = %d \n", p, p);

q = get_str2();
printf("q = %s, q = %d", q, q);

printf("\n");
return 0;
}
/*
p = abcdef1, p = 226439038
q = abcdef2, q = 226439046
*/

全局区分析

05_全局区分析

堆栈区分析

Code
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
51
52
53
54
55
56
57
58
59
60
61
62
63
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


//栈
char *get_str()
{
char str[] = "abcdedsgads"; //"abcdedsgads"在常量区,但是会拷贝一份到 栈区str[],函数运行完会释放栈区str[]
printf("str = %s\n", str);

return str;
}

//堆
char *get_str2()
{
char *tmp = (char *)malloc(100);
if (tmp == NULL)
{
return NULL;
}

strcpy(tmp, "adsagldsjglk");

return tmp;
}

int main(void)
{
char buf[128] = { 0 };

//strcpy(buf, get_str());
//printf("buf = %s\n", buf); //乱码,不确定,因为栈区数据在函数get_str调用完会释放

//解决办法:申请堆区

char *p = NULL;
p = get_str2();
if (p != NULL)
{
printf("p = %s\n", p);

//堆区要手动释放
//free是一个标识位,表示p这个空间可以被使用
free(p);
//把指针赋值为空(不然它就指向一个已释放的空间地址)
p = NULL;

//已经释放的空间不能再free(空间使用权已经交出去了), 否则报错
if (p != NULL)
{
free(p);
}

}



printf("\n");
return 0;
}

栈区分析
06栈区分析

堆区分析
04_堆区分析

1.4 函数的调用模型

栈:先进后出

屏幕快照 2018-09-06 下午11.21.49

1.5 函数调用变量传递分析

屏幕快照 2018-09-06 下午11.26.02

Code
1
2
3
4
5
6
7
8
1. main 函数中可以在栈/堆/全局分配内存,都可以被 func1 和 func2 使用

2. func2 在栈上分配的内存,不能被 func1 和 main 函数使用

3. func2 中 malloc 的内存(堆),可以被 main 和 func1 函数使用

**
4. func2 中全局分配“abcdefg”(常量全局区)内存,可以被 func1 和 main 函数使用

1.6 栈的生长方向和内存存放方向

屏幕快照 2018-09-06 下午11.46.38

Code
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
#include <stdio.h>

int main(void)
{

//栈区
int a;
int b;

//栈的生长方向:从高到底
printf("&a = %ld, &b = %ld\n", &a, &b);

//堆的生长方向:与栈相反


//内存存放方向:低 > 高
int buf[100];
printf("buf: %ld, buf+1:%ld\n", buf, buf+1);


printf("\n");
return 0;
}
/*
&a = 140732802033880, &b = 140732802033876
buf: 140732802033888, buf+1:140732802033892
*/

2 指针强化

强化 1:指针是一种数据类型

Code
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
#include <stdio.h>

int main(void)
{
int *p = 0x11111;
char ***************q = 0x11;

printf("sizeof(p) = %d, sizeof(q) =%d\n", sizeof(p), sizeof(q));


int a = 100;
int *p1 = NULL;

//指针指向谁,就把谁的地址赋值给指针
p1 = &a;
//*钥匙,通过*可以找到指针指向的内存区域,操作还是内存
*p1 = 22;

//*放在=左边,给内存赋值,写内存
//*放在=右边,取内存的值,读内容
int b = *p1;
printf("a = %d\n", a);
printf("b = %d\n", b);

printf("\n");
return 0;
}
/*
sizeof(p) = 8, sizeof(q) =8
a = 22
b = 22
*/

指针变量和它指向的内存块是两个不同的概念

Code
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
51
52
53
54
55
56
57
58
59
60
61
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
char *p = NULL;
char buf[] = "abcdef";

printf("p1 = %d\n", p);
//改变指针变量的值
p = buf;
printf("p2 = %ld\n", p);

//指针变量,和指向指向的内存是两个不同的概念
p = p + 1; //改变了指向变量的值,改变了指针的指向
printf("p2 = %ld\n", p);
printf("buf = %s\n", buf);

printf("*p = %c\n", *p);

//改变指针指向的内存,并不会影响到指针的值
printf("改变指针指向的内存,并不会影响到指针的值\n");
buf[1] = '1';
printf("p3 = %ld\n", p);
printf("buf2 = %s\n", buf);

*p = 'm';
printf("p4 = %ld\n", p);
printf("buf3 = %s\n", buf);

//写内存时,一定要确保内存可写
char *buf2 = "sadgkdsjlgjlsdk"; //文字常量区,内存不可改
//buf2[2] = '1'; //err

char buf3[] = "skgjkdsjgjds";
buf3[1] = '1'; //ok


//不允许向 NULL 和未知非法地址拷贝内存。

char *p3 = NULL;
p3 = buf3;
//给p3指向的内存区域拷贝内存
strcpy(p3, "1111"); //err

printf("\n");
return 0;
}
/*
p1 = 0
p2 = 140732889754241
p2 = 140732889754242
buf = abcdef
*p = b
改变指针指向的内存,并不会影响到指针的值
p3 = 140732889754242
buf2 = a1cdef
p4 = 140732889754242
buf3 = amcdef
*/

当我们不断的给指针变量赋值,就是不断的改变指针变量

Code
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
51
52
53
54
55
56
57
58
59
60
61
62
63
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
char *p = NULL;
char *q = NULL;
int i = 0;

char buf[100] = "akgjdksjg";

p = &buf[0];
printf("p1 = %ld, %c\n", p, *p);

p = &buf[1];
printf("p2 = %ld, %c\n\n", p, *p);

for (i = 0; i < strlen(buf); i++)
{
//p = &buf[i];
p = buf + i;
printf("p3 = %ld, %c\n", p, *p);
}

q = (char *)malloc(100);
if (q == NULL)
{
return -1;
}
strcpy(q, "abcdefg");



printf("\n");
for (i = 0; i < strlen(q); i++)
{
p = q + i;
printf("%c ", *p);
}


printf("\n");
return 0;
}
/*
p1 = 140732771035712, a
p2 = 140732771035713, k

p3 = 140732771035712, a
p3 = 140732771035713, k
p3 = 140732771035714, g
p3 = 140732771035715, j
p3 = 140732771035716, d
p3 = 140732771035717, k
p3 = 140732771035718, s
p3 = 140732771035719, j
p3 = 140732771035720, g

a b c d e f g

*/

强化 2:间接赋值(*p)是指针存在的最大意义

通过指针间接赋值

Code
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int get_a()
{
int a = 10;

return a;
}

void get_a2(int b)
{
b = 22;
}

void get_a3(int *p)
{
*p = 33; //通过*操作内存
}

void get_a4(int *a1, int *a2, int *a3, int *a4)
{
*a1 = 1;
*a2 = 2;
*a3 = 3;
*a4 = 4;
}



int main01(void)
{
int a = 100; //两个变量
int *p = NULL;

//建立关系
//指针指向谁,就把谁的地址赋值给指针
p = &a;

//通过*操作内存
*p = 22;

/*通过指针间接赋值
1、两个变量
2、建立关系
3、通过*操作内存
*/

int b = get_a();
printf("b ===== %d\n", b);

get_a2(b);
printf("b2 ===== %d\n", b);

//** 如果想通过形参改变实参的内存内容(值),必须地址传递

//实参,形参
get_a3(&b); //在函数调用时,建立关系
printf("b3 ===== %d\n", b);

int a1, a2, a3, a4;
get_a4(&a1, &a2, &a3, &a4);
printf("a1 = %d,a2 = %d, a3 = %d, a4 = %d\n", a1, a2, a3, a4);

printf("\n");
return 0;

/*
b ===== 10
b2 ===== 10
b3 ===== 33
a1 = 1,a2 = 2, a3 = 3, a4 = 4
*/
}

void fun2(int *p)
{
p = 0xaabb;
printf("fun2:p = %p\n", p);
}

void fun3(int **p)
{
*p = 0xeeff;
}

int main(void)
{
//一个变量,应该定义一个怎么类型的指针保存它的地址
//在原来类型基础上加一个*
// int a = 10;
// int *p = &a;
// int **q = &p;
//
// int ************t = NULL;
// int * ************t2 = &t;

int *p = 0x1122;
printf("p1 = %p\n", p);


fun2(p); //值传递
printf("p2 = %p\n", p);

fun3(&p); //地址传递
printf("p2 = %p\n", p);


printf("\n");
return 0;

/*
p1 = 0x1122
fun2:p = 0xaabb
p2 = 0x1122
p2 = 0xeeff
*/
}

强化 3:理解指针必须和内存四区概念相结合

指针做参数输入输出特性

Code
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void fun(char *p /* in */)
{
//给p指向的内存区域拷贝
strcpy(p, "abcddsgsd");
}

void fun2(char *p)
{
if (p == NULL)
{
return;
}

//给p指向的内存区域拷贝
strcpy(p, "abcddsgsd");
}

void fun3(char **p /* out */, int *len)
{
if (p == NULL)
{
return;
}

char *tmp = (char *)malloc(100);
if (tmp == NULL)
{
return;
}
strcpy(tmp, "adlsgjldsk");

//间接赋值
*p = tmp;
*len = strlen(tmp);


}

int main(void)
{
//输入,主调函数分配内存
char buf[100] = { 0 };
fun(buf);
printf("buf = %s\n", buf);

char *str = NULL;
fun2(str);

//输出,被调用函数分配内存,地址传递
// p的指向是null,但p变量本身不null
char *p = NULL;
int len = 0;
fun3(&p, &len);
if (p != NULL)
{
printf("p = %s, len = %d\n", p, len);
}



printf("\n");
return 0;

/*
buf = abcddsgsd
p = adlsgjldsk, len = 10
*/
}

强化 4:应用指针必须和函数调用相结合(指针做函数参数)

3 字符串

3.1 字符串的基本操作

字符串的初始化

Code
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <stdio.h>
#include <string.h>

/*
c语言没有字符串类型,通过字符数组模拟
C语言字符串,以字符‘\0’, 数字0
*/
int main01(void)
{
//不指定长度, 没有0结束符,有多少个元素就有多长
char buf[] = { 'a', 'b', 'c' };
printf("buf = %s\n", buf);

//指定长度,后面没有赋值的元素,自动补0
char buf2[100] = { 'a', 'b', 'c' };
printf("buf2 = %s\n", buf2);

//所有元素赋值为0
char buf3[100] = { 0 };

//char buf4[2] = { '1', '2', '3' };//数组越界

char buf5[50] = { '1', 'a', 'b', '0', '7' };
printf("buf5 = %s\n", buf5);

//%s遇到数字0 就默认结束
char buf6[50] = { '1', 'a', 'b', 0, '7' };
printf("buf6 = %s\n", buf6);

char buf7[50] = { '1', 'a', 'b', '\0', '7' };
printf("buf7 = %s\n", buf7);


//使用字符串初始化,常用
char buf8[] = "agjdslgjlsdjg";
//strlen: 测字符串长度,不包含数字0,字符'\0'
//sizeof:测数组长度,包含数字0,字符'\0'
printf("strlen = %d, sizeof = %d\n", strlen(buf8), sizeof(buf8));

char buf9[100] = "agjdslgjlsdjg";
printf("strlen = %d, sizeof = %d\n", strlen(buf9), sizeof(buf9));

printf("test");
//\012相当于\n
char str[] = "\0129";
printf("%s\n", str);


printf("\n");
return 0;

/*
buf = abc
buf2 = abc
buf5 = 1ab07
buf6 = 1ab
buf7 = 1ab
strlen = 13, sizeof = 14
strlen = 13, sizeof = 100
test
9
*/
}

int main(void)
{
char buf[] = "algjdlksajgldksjg";
int i = 0;
int n = strlen(buf);
char *p = NULL;

//[]方式
for (i = 0; i < n; i++)
{
printf("%c", buf[i]);
}
printf("\n");

//指针方法
//数组名字,数组首元素地址
p = buf;
for (i = 0; i < n; i++)
{
printf("%c", p[i]);
}
printf("\n");

for (i = 0; i < n; i++)
{
printf("%c", *(p+i) );
}
printf("\n");

for (i = 0; i < n; i++)
{
printf("%c", *(buf+i) );
}
printf("\n");

//buf和p完全等价吗?
//p++;
//buf++;
//buf只是一个常量,不能修改

/*
algjdlksajgldksjg
algjdlksajgldksjg
algjdlksajgldksjg
algjdlksajgldksjg
*/
printf("\n");
return 0;
}

字符串拷贝函数的实现

Code
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main01(void)
{
char src[] = "abcedfdgds";
char dst[100] = { 0 };
int i = 0;


for (i = 0; src[i] != '\0'; i++)
{
dst[i] = src[i];
}
//补结束符
dst[i] = 0;

printf("%s\n", dst);

printf("\n");
return 0;
}

void my_strcpy(char *dst, char *src)
{
int i = 0;

for (i = 0; *(src+i) != '\0'; i++)
{
*(dst+i) = *(src+i); //dst[i] = src[i]
}
//补结束符
//dst[i] = 0;
*(dst + i) = 0;
}

void my_strcpy2(char *dst, char *src)
{
while (*src != 0)
{
*dst = *src;
dst++;
src++;
}
*dst = 0;
}

void my_strcpy3(char *dst, char *src)
{
//*dst = *src
//dst++, src++
//判断 *dst是否为0, 为0跳出循环
while (*dst++ = *src++)
{
NULL;
}

}

//成功为0,失败非0
//1、判断形参指针是否为NULL
//2、最好不要直接使用形参
int my_strcpy4(char *dst, char *src)
{
if (dst == NULL || src == NULL)
{
return -1;
}

//辅助变量把形参接过来
char *to = dst;
char *from = src;

//*dst = *src
//dst++, src++
//判断 *dst是否为0, 为0跳出循环
while (*to++ = *from++)
{
NULL;
}

printf("my_strcpy4: dst = %s\n", dst);

return 0;
}

int main(void)
{
char src[] = "abcedfdgds";
char dst[100] = { 0 };
int ret = 0;

ret = my_strcpy4(dst, src);
if (ret != 0)
{
printf("my_strcpy4 err:%d\n", ret);
return ret;
}
printf("%s\n", dst);


printf("\n");
return 0;
}

3.2 项目开发常用字符串应用模型

strstr中的while和do-while模型

Code
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


//查找子串出现的次数
int main01(void)
{
char *p = "11abcd111122abcd333abcd3322abcd3333322qqq";
int n = 0;

do
{
p = strstr(p, "abcd");
if (p != NULL)
{
n++; //累计个数

//重新设置查找的起点
p = p + strlen("abcd");

}
else //如果没有匹配的字符串,跳出循环
{
break;
}


} while (*p != 0); //如果没有到结尾

printf("n = %d\n", n);

printf("\n");
return 0;
}


int main02(void)
{
char *p = "11abcd111122abcd333abcd3322abcd3333322qqq";
int n = 0;

while ( ( p = strstr(p, "abcd") ) != NULL )
{
//能进来,肯定有匹配的子串

//重新设置起点位置
p = p + strlen("abcd");
n++;

if (*p == 0) //如果到结束符
{
break;
}

}

printf("n = %d\n", n);

printf("\n");
return 0;
}

int my_strstr(char *p, int *n)
{
//辅助变量
int i = 0;
char *tmp = p;

while ((tmp = strstr(tmp, "abcd")) != NULL)
{
//能进来,肯定有匹配的子串

//重新设置起点位置
tmp = tmp + strlen("abcd");
i++;

if (*tmp == 0) //如果到结束符
{
break;
}

}

//间接赋值
*n = i;

return 0;
}

int main(void)
{

char *p = "11abcd111122abcd333abcd3322abcd3333322qqq";
int n = 0;
int ret = 0;

ret = my_strstr(p, &n);
if (ret != 0)
{
return ret;
}
printf("n = %d\n", n);



printf("\n");
return 0;
}

两头堵模型

Code
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

//去首尾空格 求字符串长度
int main01(void)
{
char *p = " abcdefg ";
int begin = 0;
int end = strlen(p) - 1;
int n = 0;

//从左边开始
//如果当前字符为空,而且没有结束
while ( p[begin] == ' ' && p[begin] != 0)
{
begin++; //位置从右移动一位
}

//如果当前字符为空,而且没有结束
while ( p[end] == ' ' && p[end] != 0)
{
end--; //往左移动
}

n = end - begin + 1;
printf("n = %d\n", n);

printf("\n");
return 0;
}

int fun(char *p, int *n)
{
if (p == NULL || n == NULL)
{
return -1;
}

int begin = 0;
int end = strlen(p) - 1;

//从左边开始
//如果当前字符为空,而且没有结束
while (p[begin] == ' ' && p[begin] != 0)
{
begin++; //位置从右移动一位
}

//如果当前字符为空,而且没有结束
while (p[end] == ' ' && p[end] != 0)
{
end--; //往左移动
}

*n = end - begin + 1;

return 0;
}

int fun2(char *p, char *buf)
{
if (p == NULL || buf == NULL)
{
return -1;
}

int begin = 0;
int end = strlen(p) - 1;
int n = 0;

//从左边开始
//如果当前字符为空,而且没有结束
while (p[begin] == ' ' && p[begin] != 0)
{
begin++; //位置从右移动一位
}

//如果当前字符为空,而且没有结束
while (p[end] == ' ' && p[end] != 0)
{
end--; //往左移动
}

n = end - begin + 1; //非空元素个数


strncpy(buf, p + begin, n);
buf[n] = 0;

return 0;
}

int main(void)
{
char *p = " abcddsgadsgefg ";
int ret = 0;
char buf[100] = { 0 };

//将去首尾的串拷贝给buff
ret = fun2(p, buf);
if (ret != 0)
{
return ret;
}
printf("buf = %s\n", buf);


printf("\n");
return 0;
}

3.3 const

const的使用

Code
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct MyStruct
{
int a;
int b;
}MyStruct;

void fun(MyStruct *p)
{
//指针能变
//p = NULL;
//指针指向的内存也可以变
//p->a = 10; //ok
}

void fun1(MyStruct const *p)
{
//p = NULL; //ok
//p->a = 10; //err
}

void fun2(MyStruct * const p)
{
//p = NULL; //err
p->a = 10;//ok
}

//p只读
void fun3(MyStruct const * const p)
{
MyStruct tmp;
tmp.a = p->a;
}

int main(void)
{
//const修饰一个变量为只读
const int a = 10;
//a = 100; //err

//指针变量, 指针指向的内存, 2个不同概念
char buf[] = "aklgjdlsgjlkds";

//从左往右看,跳过类型,看修饰哪个字符
//1 如果是*, 说明指针指向的内存不能改变
//2 如果是指针变量,说明指针的指向不能改变,指针的值不能修改
const char *p = buf;//1
// 等价于上面 char const *p1 = buf;
//p[1] = '2'; //err
p = "agdlsjaglkdsajgl"; //ok

char * const p2 = buf;
p2[1] = '3';
//p2 = "salkjgldsjaglk"; //err

//p3为只读,指向不能变,指向的内存也不能变
const char * const p3 = buf;

//如何引用另外.c中const变量,了解
//extern const int aa; //不能再赋值,只能声明
//printf("aa = %d\n", aa);

//const修饰的变量,只能定义时初始化

//在c语言中, const是一个冒牌货
const int b = 10;
//b = 100; //err(不能直接修改)
//但可以通过指针间接修改
int *q = &b;
*q = 22;
printf("%d, %d\n", b, *q);


printf("\n");
return 0;
}

4 二级指针

屏幕快照 2018-09-07 下午8.48.09

4.1 二级指针输出特性

二级指针做参数输出特性

Code
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
51
52
53
54
55
56
57
58
59
60
61
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int getMem(char *p)
{
p = (char *)malloc(sizeof(char)* 100);
if (p == NULL)
{
return -1;
}
strcpy(p, "abdakg");
printf("p = %s\n", p);

return 0;
}

int getMem2(char **p)
{
if (p == NULL)
{
return -1;
}

char *tmp = (char *)malloc(100);
if (tmp == NULL)
{
return -2;
}

strcpy(tmp, "ALGKJDLSJGLKDSJ");

*p = tmp;

return 0;
}

int main(void)
{
char *p = NULL;
int ret = 0;

ret = getMem2(&p);
if (ret != 0)
{
printf("getMem err: %d\n", ret);
return ret;
}

printf("p = %s\n", p);

if (p != NULL)
{
free(p);
p = NULL;
}

printf("\n");
return 0;
}

4.2 二级指针输入特性

二级指针做输入:第一种内存模型

Code
1
2
3
4
5
//指针数组
char *myArray[] = {"aaaaaa", "ccccc", "bbbbbb", "111111"};

void printMyArray(char **myArray, int num);
void sortMyArray(char **myArray, int num);
Code
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//指针数组
int main01(void)
{

//每个类型都是char *
char *p0 = "111111111";
//printf("%s\n", p0);
char *p1 = "000000000";
char *p2 = "bbbbbbbbb";
char *p3 = "aaaaaaaaa";

//指针数组,指针的数组,它是一个数组,每一个元素都是指针char *
char *p[] = { "111111111", "000000000", "bbbbbbbbb", "aaaaaaaaa" };
//p[0] = "111111111"
int n = sizeof(p) / sizeof(p[0]);
printf("sizeof(p) = %d, sizeof(p[0]) = %d\n", sizeof(p), sizeof(p[0]));

int i = 0;
for (i = 0; i < n; i++)
{
printf("%s\n", p[i]);
}

char *q[10] = { "111111111", "000000000", "bbbbbbbbb", "aaaaaaaaa" };
printf("sizeof(q) = %d, sizeof(q[0]) = %d\n", sizeof(q), sizeof(q[0]));



printf("\n");
return 0;
}

int main02(void)
{
//指针数组,指针的数组,它是一个数组,每一个元素都是指针char *
char *p[] = { "111111111", "000000000", "bbbbbbbbb", "aaaaaaaaa" };
//char **q = { "111111111", "000000000", "bbbbbbbbb", "aaaaaaaaa" }; //err
//p[0] = "111111111"
int n = sizeof(p) / sizeof(p[0]);
int i = 0;
int j = 0;
char *tmp = NULL;

printf("排序前:\n");
for (i = 0; i < n; i++)
{
printf("%s, ", p[i]);
}
printf("\n");

//选择法排序
for (i = 0; i < n - 1; i++)
{
for (j = i + 1; j < n; j++)
{
if (strcmp(p[i], p[j]) > 0)
{
tmp = p[i];
p[i] = p[j];
p[j] = tmp;
}
}
}

printf("\n排序后:\n");
for (i = 0; i < n; i++)
{
printf("%s, ", p[i]);
}
printf("\n");


printf("\n");
system("pause");
return 0;
}

void test(int a[])
//void test(int *a)
{

}

void fun()
{
int a[10];
test(a);


}

//void print_array(char *p[], int n)
void print_array(char **p, int n)
{
int i = 0;
for (i = 0; i < n; i++)
{
printf("%s, ", p[i]);
}
printf("\n");
}

void sort_array(char **p, int n)
{
int i, j;
char *tmp;
for (i = 0; i < n - 1; i++)
{
for (j = i + 1; j < n; j++)
{
if (strcmp(p[i], p[j]) > 0)
{
tmp = p[i];
p[i] = p[j];
p[j] = tmp;
}
}
}
}

int main03(void)
{
//指针数组,指针的数组,它是一个数组,每一个元素都是指针char *
char *p[] = { "111111111", "000000000", "bbbbbbbbb", "aaaaaaaaa" };
//char **q = { "111111111", "000000000", "bbbbbbbbb", "aaaaaaaaa" }; //err
//p[0] = "111111111"
int n = sizeof(p) / sizeof(p[0]);

printf("排序前:\n");
print_array(p, n);

sort_array(p, n);

printf("\n排序后:\n");
print_array(p, n);


printf("\n");
system("pause");
return 0;
}

二级指针做输入:第二种内存模型

c
1
2
3
4
char myArray[10][30] = {"aaaaaa", "ccccc", "bbbbbbb", "1111111111111"};

void printMyArray(char myArray[10][30], int num);
void sortMyArray(char myArray[10][30], int num);
Code
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main01(void)
{
char a0[30] = "22222222222";
char a1[30] = "11111111111";
char a2[30] = "bbbbbbbbbbb";
char a3[30] = "aaaaaaaaaaaa";

//4个a[30]的一维数组,二维数组
//定义二维数组,不写第一个[]值有条件, 必须要初始化
//a代表首行地址,首行地址和首行首元素地址有区别,但是他们的值是一样
//区别:步长不一样
char a[][30] = { "22222222222", "11111111111", "bbbbbbbbbbb", "aaaaaaaaaaaa" };
printf("a:%d, a+1: %d\n", a, a+1);

char b[30];
printf("&b:%d, &b+1:%d\n", &b, &b+1);
printf("b:%d, b+1:%d\n", b, b+1);

int n = sizeof(a) / sizeof(a[0]);
printf("n = %d\n", n);


//a[0] = "22222222222"
int i = 0;
for (i = 0; i < 4; i++)
{
printf("%s\n",a[i]); //首行地址,和首行首元素地址的值是一样
// a+i, *(a+i)
}



printf("\n");
system("pause");
return 0;
}

//不能通过 char ** 作为函数形参,因为指针+1步长不一样
// char **,指针+1步长为 4 个字节
// char a[][30],指针+1步长为 1 行的长度,这里为 30 个字节
void print_array_err(char **a, int n)
{
printf("a: %d, a+1:%d\n", a, a+1);
int i = 0;
for (i = 0; i < n; i++)
{
//printf("%s\n", a[i]); //首行地址,和首行首元素地址的值是一样
// a+i, *(a+i)
}
}

void print_array(char a[][30], int n)
{
//printf("a: %d, a+1:%d\n", a, a + 1);
int i = 0;
for (i = 0; i < n; i++)
{
printf("%s, ", a[i]); //首行地址,和首行首元素地址的值是一样

}
printf("\n");
}

void sort_array(char a[][30], int n)
{
int i = 0;
int j = 0;
char tmp[30];

for (i = 0; i < n - 1; i++)
{
for (j = i + 1; j < n; j++)
{
if (strcmp(a[i], a[j])> 0)
{
//交换的内存块
strcpy(tmp, a[i]);
strcpy(a[i], a[j]);
strcpy(a[j], tmp);
}
}
}

}

int main(void)
{
char a[][30] = { "22222222222", "11111111111", "bbbbbbbbbbb", "aaaaaaaaaaaa" };
int n = sizeof(a) / sizeof(a[0]);

printf("before sort:\n");
print_array(a, n);

sort_array(a, n);

printf("\nafter sort:\n");
print_array(a, n);

printf("\n");
system("pause");
return 0;
}

二级指针做输入:第三种内存模型

c
1
2
3
4
5
6
char **myArray = NULL;

char **getMem(int num);
void printMyArray(char **myArray, int num);
void sortMyArray(char **myArray, int num);
void arrayFree(char **myArray, int num);
Code
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main01(void)
{
char *p0 = NULL;
p0 = (char *)malloc(100);
strcpy(p0, "agdsgds");

//10个char *, 每一个的值都是空
int i = 0;
char *p[10] = { 0 };
for (i = 0; i < 10; i++)
{
p[i] = malloc(100);
strcpy(p[i], "agdsg");
}

int a[10];
int *q = (int *)malloc(10 * sizeof(int)); //q[10]


//动态分配一个数组,每个元素都是char *
//char *ch[10]
int n = 3;
char **buf = (char **)malloc(n * sizeof(char *)); //char *buf[3]
if (buf == NULL)
{
return -1;
}
for (i = 0; i < n; i++)
{
buf[i] = (char *)malloc(30 * sizeof(char));
char str[30];
sprintf(str, "test%d%d", i, i);
strcpy(buf[i], str);
}

for (i = 0; i < n; i++)
{
printf("%s, ", buf[i]);
}
printf("\n");

for (i = 0; i < n; i++)
{
free(buf[i]);
buf[i] = NULL;
}

if (buf != NULL)
{
free(buf);
buf = NULL;
}

printf("\n");
system("pause");
return 0;
}

char **getMem(int n)
{
int i = 0;
char **buf = (char **)malloc(n * sizeof(char *)); //char *buf[3]
if (buf == NULL)
{
return NULL;
}
for (i = 0; i < n; i++)
{
buf[i] = (char *)malloc(30 * sizeof(char));
char str[30];
sprintf(str, "test%d%d", i, i);
strcpy(buf[i], str);
}

return buf;
}

void print_buf(char **buf, int n)
{
int i = 0;
for (i = 0; i < n; i++)
{
printf("%s, ", buf[i]);
}
printf("\n");
}


void free_buf(char **buf, int n)
{
int i = 0;
for (i = 0; i < n; i++)
{
free(buf[i]);
buf[i] = NULL;
}

if (buf != NULL)
{
free(buf);
buf = NULL;
}

}


int main(void)
{
char **buf = NULL;
int n = 3;

buf = getMem(n);
if (buf == NULL)
{
printf("getMem err\n");
return -1;
}

print_buf(buf, n);

free_buf(buf, n);
buf = NULL;

printf("\n");
system("pause");
return 0;
}

5 多维数组

一维数组

Code
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{

//int b[]; //定义时必须初始化
int a1[100] = { 1, 2, 3, 4 }; //没有赋值的都为0

int a[] = { 1, 2, 3, 4, 5, 6, 7, 8};
//sizeof()测变量所占的空间(变量所对应类型的空间)

int n = 0;
int i = 0;
// sizeof(a) = 4 * 8 = 32 数组类型:由元素个数 和元素类型对应 int [8]
//sizeof(a[0]) 首元素大小,每个元素4个字节
n = sizeof(a) / sizeof(a[0]); //n = 8

for (i = 0; i < n; i++)
{
//*(a+i) : a+i代表第I元素地址, *(a+i)指针指向的内存(取值)
//[] * 等级
printf("%d ", *(a + i));
}
printf("\n");

//数组类型
//a代表首元素地址
//&a代表整个数组首地址,它和首元素地址一样的,但是,它们步长不一样
printf("a:%d, a+1:%d\n", a, a+1); //+4
printf("&a: %d, &a+1:%d\n", &a, &a + 1); //+32

//数组类型:由元素个数 和元素类型对应 int [8]
//通过typedf定义一个数组类型
//有typedef是类型,没有是变量
typedef int A[8]; //代表数组类型,它是一类型,不是变量
//typedef int (A)[8];

A b; // int b[8], 去了typedef, b替换到A的位置

for (i = 0; i < 8; i++)
{
b[i] = 2*i + 1;
}

for (i = 0; i < 8; i++)
{
//printf("%d ", b[i]);
printf("%d ", *(b+i));
}
printf("\n");

printf("b: %d, b+1:%d\n", b, b+1);
printf("&b: %d, &b+1:%d\n", &b, &b + 1);




printf("\n");
system("pause");
return 0;
}

数组指针

Code
1
2
3
4
5
//有typedef:类型
//没有typedef:变量
1、根据数组类型,定义指针变量,数组指针变量
2、直接定义数组指针变量(常用)
3、先定义数组指针类型,再根据类型定义指针变量(常用)
Code
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//argc: 传参数的个数(包含可执行程序)
//argv:指针数组,指向输入的参数
int main01(int argc, char *argv[])
{
//数组指针,指针数组

//指针数组,它是数组,每个元素都是指针
//[] 比 * 优先级高
char *a[] = { "aaaaaaa", "bbbbbbbbbb", "ccccccc" };
int i = 0;

printf("argc = %d\n", argc);

for (i = 0; i < argc; i++)
{
printf("%s\n", argv[i]);
}


printf("\n");
system("pause");
return 0;
}

//1、根据数组类型,定义指针变量,数组指针变量
int main02(void)
{
//数组指针,它是指针,指向一个数组的指针
//数组指针,指向一维数组的整个数组,而不是首元素地址
//定义数组指针变量

int a[10] = { 0 };
int i = 0;

//1、先定义数组类型,根据类型定义指针变量
typedef int A[10]; //A数组类型, [10]代表步长
//p是指针,指向一个步长10的数组,p是数组指针
A *p = NULL; //p数组指针类型变量
//p = a; //a代表首元素地址, a 和 &a 一样,最终也是当做&a,警告
p = &a; //&a代表整个数组首地址

printf("p:%d, p+1:%d\n", p, p + 1);

for (i = 0; i < 10; i++)
{
//a[]
// p = &a;
// *p = *&a -> a
(*p)[i] = i + 1;
}

for (i = 0; i < 10; i++)
{
printf("%d ", (*p)[i] );
}
printf("\n");



printf("\n");
system("pause");
return 0;
}

//3、先定义数组指针类型,再根据类型定义指针变量(常用)
int main(void)
{
int a[10] = { 0 };
int i = 0;

//2、先定义数组指针类型,根据类型定义变量
//和指针数组写法很类似,多了()
//() 和 []优先级一样,从左往右
//()有指针,它是一个指针, []
//指向数组的指针,它有typedef,所以它是一个数组指针类型

//int[2][3] 一个长度为2的数组,每个数组存放的类型是长度为3的数组

typedef int(*P)[10];//(*P)是指针,[10]代表步长为10,该指针指向一个步长10的数组,所以是数组指针类型
P q; //数组指针变量
q = &a;

for (i = 0; i < 10; i++)
{
// *q = *&a -> a
(*q)[i] = 2*i + 1;
}

for (i = 0; i < 10; i++)
{
printf("%d ", (*q)[i]);
}
printf("\n");


printf("\n");
system("pause");
return 0;
}

void fun(void * a)
{
printf("%d\n", (int)a);
}

//2、直接定义数组指针变量(常用)
int main04(void)
{
int t = 10;
fun((void *)t);


int a[10] = { 0 };
//a[11] = 10;

int i = 0;

//2、直接定义数组指针变量
//和指针数组写法很类似,多了()
//() 和 []优先级一样,从左往右
//()有指针,它是一个指针, []
//指向数组的指针, 没有typedef,所以它是一个数组指针变量
int(*q)[10]; //q数组指针变量
q = &a; //q指向a数组

for (i = 0; i < 10; i++)
{
(*q)[i] = 3 * i + 1;
}

for (i = 0; i < 10; i++)
{
printf("%d ", (*q)[i]);
}
printf("\n");



printf("\n");
system("pause");
return 0;
}

多维数组名的本质

c
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
1)二维数组初始化
int a1[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};

int a2[3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

int a3[][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };


2)内存中并不存在多维数组,多维数组在内存中都是线性存储
int a[3][5] = { 0 };
int *b = (int *)a;
int i = 0;
for(i = 0; i < 15; i++)
{
printf("%d ", b[i]);
}


3)多维数组名

//学会类比
int b[5] = {0};
b: 首行首元素地址(一级指针), +1,跳 4 个字节
&b:首行地址(二级指针),+1,跳 4*5 = 20个字节

//二维数组实际上就是 N 个一维数组
//把二维数组第一个[]的值看做标志位,0 -> 2
//第0个一维数组a[5] -> 第2个一维数组a[5]
int a[3][5] = { 0 };
a:
二维数组首元素地址
代表首行地址,相当于一维数组整个数组的地址,相当于上面的 &b,本来就是一个二级指针

//(重要)首行地址 --> 首行首元素地址(加*)
*a:首行首元素地址,相当于一维数组首元素地址,相当于上面的 b

a + i -> &a[i]: 第i行地址

//(重要)某行地址 --> 某行首元素地址(加*)
*(a+i) -> *&a[i] -> a[i]: 第i行首元素地址

//第i行j列元素的地址,某行首元素地址 + 偏移量
*(a+i)+j -> a[i]+j -> &a[i][j]: 第i行j列元素的地址

//第i行j列元素的值,第i行j列元素的地址的基础上(加 *)
*(*(a+i)+j) -> a[i][j]: 第i行j列元素的值


int a[3][5] = { 0 };
sizeof(a): 二维数组整个数组长度,4 * 3 * 5 = 60
sizeof(a[0]):a[0]为第0行首元素地址,相当于测第0行一维数组的长度:4 * 5 = 20
sizeof(a[0][0]):a[0][0]为第00列元素(是元素,不是地址),测某个元素长度:4字节

4)多维数组名,实际上是一个数组指针,指向数组的指针,步长为一行字节长度

int a[3][5] = { 0 };

//定义一个数组指针类型的变量
int(*p)[5];

//编译会有警告,但不会出错,因为 a 和 &a的值一样
//但是&a代表整个二维数组的首地址
//就算p = &a这样赋值,编译器内部也会自动转换为 p = a
//不建议这么做
p = &a;

//a 本来就是第0个一维数组整个数组的地址,所以,无需加&
p = a;


5)二维数组做形参的三种形式
//一维数组做函数参数退化为一级指针
//二维数组(多维数组)做函数参数,退化为数组指针

int a[3][5] = { 0 };

void print_array1(int a[3][5]);

//第一维的数组,可以不写
//第二维必须写,代表步长,确定指针+1的步长 5*4
void print_array2(int a[][5])

//形参为数组指针变量,[]的数字代表步长
void print_array3(int (*a)[5]);

//a+1和二维数组的步长不一样
//这里的步长为4
//上面二维数组的步长为 5 * 4 = 20
void print_array3(int **a); //err
文章作者: Machine
文章链接: https://machine4869.gitee.io/2018/09/06/15362416453168/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 哑舍
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论