C语言 指针和数组
Dark And Grey 人气:0指针 和 数组 试题解析 小编,在这里想说一下,c语言的最后一节 C预处理,可能还需要一些时间,因为小编,昨天才下载了虚拟机 和 linux 系统,还没开始安装。所以无法着手写,因为 C预处理,vs2013很难表达,也就意味可能会讲不清楚。所以这篇文章可能需要点时间,再加上小编初期的文章,是没有排版的(而且可能有些错误,请大家以重置版为准),所以这几天我就把这些重新写。有兴趣的朋友可以看看。(ps:如果哪一天没有更新,意味着小编正在努力学习,为了能给大家呈现一片详细好懂的文章。)
下面直接进入正文
数组题:
先给你们打个底
1. sizeof(数组名) ,此时的 数组名 代表的是 整个数组。
2. &数组名,此时的 数组名 代表的是 整个数组。
除了以上两种特殊情况,其它的,几乎 100% 代表的是首元素地址
另外有一点请记住: arr[ i ] == *(arr + i ); arr[ i ][ j ] == *( *(arr + i)+ j)
好的,现在就让我们进入习题中,去磨练这份知识,让它再我们脑子里扎根
程序一(一维数组):
一维数组 #include<stdio.h> int main() { int a[] = {1,2,3,4}; printf("%d\n", sizeof(a));// 16 计算的是整个数组的内存大小 // sizeof(数组名) 和 &数组名,此时数组名,代表的是整个数组 printf("%d\n", sizeof(a+0));// 输出为 4 / 8 (地址所占大小只与多少位操作系统有关,4byte【32位】,8byte【64】) // 因为 sizeof()的括号里放的不是数组名,而是 首元素地址 + 0,即sizeof(a[0]) printf("%d\n", sizeof(*a));// 4 因为 a 没有 &(取地址),没有单独放在sizeof()里(属于非特殊情况,数组名代表首元素地址) // *a 就是首元素,这里求的是 首元素 的内存大小,因为 这是一个整形数组,里面的每个元素都是 int 类型,即为 4 byte printf("%d\n", sizeof(a+1));// 4 / 8 与a + 0 的意思是一样的,只是现在这里跳过一个元素,也就是说现在这是第二个元素的地址,地址的大小无非 就是 4/8 byte(受多少位系统影响,4byte【32位】,8byte【64位】) printf("%d\n", sizeof(a[1]));// 4 a[1] == *(a+1) 第二个元素的地址对齐解引用找到第二个元素,sizeof(a[1]),就是在求 第二个元素的大小,因为 这是一个整形数组,里面的每个元素都是 int 类型,即为 4 byte printf("%d\n", sizeof(&a));// 4 / 8 &a 取出的是数组的地址,数组名没有单独放在sizeof括号里,而且 &a 取出的是整个数组的地址,sizeof(&a) 就是在爱求 数组的地址大小 // 数组的地址也是地址 ,也受多少位操作系统影响: 4 byte【32位】 / 8byte 【64位】 printf("%d\n", sizeof(*&a));// 16 &a 取出的是数组的地址,对取其解引用,找到数组名a,也就是说 * 和 & 相互抵消了,等于就是说 sizeof(数组名) ,此时数组名代表的是整个数组 printf("%d\n", sizeof(&a+1));// 4 / 8 &a,拿出的是数组a的地址,+1跳过整个数组,但还是一个地址,受多少位操作系统影响: 4 byte【32位】 / 8byte 【64位】 printf("%d\n", sizeof(&a[0]));// 4 / 8 [] 的优先级 比 & 高,a 先和 [] 结合形成一个数组,在对其取地址,又因为 a[0] == *(a+0),即这里 &a[0],取出的是 首元素的地址,既然是地址,就要受多少位操作系统影响: 4 byte【32位】 / 8byte 【64位】 printf("%d\n", sizeof(&a[0]+1));// 4 / 8 [] 的优先级 比 & 高,a 先和 [] 结合形成一个数组,在对其取地址,又因为 a[0] == *(a+0),即这里 &a[0],取出的是 首元素的地址,此时加一,跳过一个整形,也就是说 此时的地址 是 第二个元素的地址,既然是地址,就要受多少位操作系统影响: 4 byte【32位】 / 8byte 【64位】 return 0; }
字符数组
1. sizeof(数组名) ,此时的 数组名 代表的是 整个数组。
2. &数组名,此时的 数组名 代表的是 整个数组。
除了以上两种特殊情况,其它的,几乎 100% 代表的是首元素地址
另外有一点请记住: arr[ i ] == *(arr + i );
arr[ i ][ j ] == *( *(arr + i)+ j)
程序二(字符数组):
#include<stdio.h> int main() { char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' }; printf("%d\n",sizeof(arr));// 6 arr是一个字符数组,每一个元素的内存大小为 1 byte,该数组有 5个 元素,即 6 byte printf("%d\n", sizeof(arr+0));//4 / 8 arr+0 == 首元素地址,既然是地址,就要受多少位操作系统影响: 4 byte【32位】 / 8byte 【64位】 printf("%d\n", sizeof(*arr));// 1 *arr(对首元素地址进行解引用) == 首元素,此时计算的是首元素的大小,即 1 byte printf("%d\n", sizeof(arr[1]));// 1 arr[1] == *(arr+1),即第二个元素,即 sizeof 计算的是一个元素的大小,即 1 byte printf("%d\n", sizeof(&arr));// 4 / 6 &arr 取出的是 数组的地址,既然是地址,就要受多少位操作系统影响: 4 byte【32位】 / 8byte 【64位】 printf("%d\n", sizeof(&arr+1));// 4 / 8 &arr 取出的是数组的地址,在对其 加一,即跳过整个数组,此时地址是数组中不包含的地址,越界了,假设 数组有 5 个元素,下标0 ~ 4,此时的地址是 下标为 5 的地址,有人肯能会有,那这个不应该是错的吗》怎么还有大小?, //因为 sizeof 是不会真的去访问越界的位置,只是看看那个位置的数据是什么类型,并不涉及访问和计算。 //因为是第六个元素的地址,既然是地址,就要受多少位操作系统影响: 4 byte【32位】 / 8byte 【64位】 printf("%d\n", sizeof(&arr[0]+1));// 4 / 8 这个就不用我多说了,它就就是一个地址,第二个元素的地址(&arr[0] + 1 == 首元素地址 + 1,即首元素地址 挑过一个 字节,),既然是地址,就要受多少位操作系统影响: 4 byte【32位】 / 8byte 【64位】 return 0; }
接下我们把 sizeof 换成 strlen ,来看看这些题会发生什么变化? 在开始之前请注意一下内容: 1. strlen 只有 遇到 ‘\0',才会停止计数。计数个数(不包括 ‘\0' 这个元素),如果没有 ‘\0',strlen 这个函数就会往后面找 ‘\0',所以 strlen 的返回值,在没有 '\0'的情况下,返回 一个随机值。 2.strlen(这里要放一个地址),如果不是,会导致程序崩溃 举个列子:strlen(a),a 的 地址为 97,strlen 就会 以 97 为地址,开始往后 一边寻找 ‘\0',一边计数,很容易形成越界访问,从而导致程序崩溃
程序三(字符数组):
#include<stdio.h> #include<string.h> int main() { char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' }; printf("%d\n", strlen(arr));// 随机值, 没有'\0'·从首元素开始计数,且向后寻找'\0',但是该数组里没有'\0'的,所以它会一直往后找'\0',直到找到了才停止计数,所以返回的是一个随机值 printf("%d\n", strlen(arr + 0));// 随机值,还是从首元素开始计数,且向后寻找'\0',但是该数组里没有'\0'的,所以它会一直往后找'\0',直到找到了才停止计数,所以返回的是一个随机值 //printf("%d\n", strlen(*arr));// *arr == 'a' == 97 strlen(把97当成一个地址),非法访问,程序崩溃 //printf("%d\n", strlen(arr[1]));// 与上表达式一样, 非法访问,程序崩溃 printf("%d\n", strlen(&arr));// 随机值 printf("%d\n", strlen(&arr + 1));// 随机值 - 6 ,与上表示式的随机值 ,相差 6,因为 &arr 读取的是数组的地址,加一 等于 跳过一个个数组,也就是跳过了 6 个元素,也就是说这六个元素,没有被计入,现在是 第 7 个元素的地址,从第七个元素开始 计数 和 寻找 '\0' printf("%d\n", strlen(&arr[0] + 1));// 随机值 - 1,与 上上 表达式的随机值,相差 1,这里是从 第二个元素,开始 计数 与 寻找'\0',第一个元素没有计入在内 return 0; }
程序四(字符数组):
#include<stdio.h> #include<string.h> int main() { char arr[] = "abcdef";// == "abcdef" == 'a', 'b', 'c', 'd', 'e', 'f', '\0' printf("%d\n", sizeof(arr));// 7 后面隐藏一个元素 '\0' printf("%d\n", sizeof(arr + 0));// 4 / 8 是一个地址, printf("%d\n", sizeof(*arr));// 1 *arr == 首元素,这里计算的是首元素的内存大小 printf("%d\n", sizeof(arr[1]));// 1 计算第二个元素的内存大小 printf("%d\n", sizeof(&arr));// 4 / 8 ,这是一个地址 printf("%d\n", sizeof(&arr + 1));// 4 / 8 ,这是一个地址 printf("%d\n", sizeof(&arr[0]) + 1);// 4 / 8, 这是一个地址 printf("%d\n", strlen(arr));// 6 遇到'\0'停止计数,'\0'不计入在内 printf("%d\n", strlen(arr + 0));// 6 arr+0 == arr 从首元素开始计数,遇到'\0'停止计数,'\0'不计入在内 printf("%d\n", strlen(*arr));// *arr == a == 97,以97为地址( 从 97 开始计算直到遇到'\0' ),属于非法访问,程序崩溃 printf("%d\n", strlen(arr[1]));// arr[1] == b == 98,以98为地址( 从 98 开始计算直到遇到'\0' ),属于非法访问,程序崩溃 printf("%d\n", strlen(&arr));// 6 &arr 虽然是数组的地址,但是对于strlen函数来说,它就只是第一个元素的地址,从该元素开始计数,遇到'\0'停止计数,'\0'不计入在内 printf("%d\n", strlen(&arr + 1));// 随机值,跳过了一个数组,从 '\0' 后面 开始计数,直到遇到'\0'停止计数,'\0'不计入在内 printf("%d\n", strlen(&arr[0]) + 1);// 5 从第二个元素开始计数,遇到'\0'停止计数,'\0'不计入在内 return 0; }
程序五(字符数组):
#include<stdio.h> #include<string.h> int main() { char* p = "abcdef";// p 存的是 字符串的首元素的a的地址 printf("%d\n", sizeof(p));// 4 / 8 a 的地址 printf("%d\n", sizeof(p + 0));// 4 / 8 b 的地址, printf("%d\n", sizeof(*p));// 1 *arr == 首元素 a,这里计算的是首元素的内存大小 printf("%d\n", sizeof(p[0]));// 1 计算第一个元素的内存大小 p[0] == *(p+0) == a printf("%d\n", sizeof(&p));// 4 / 8 把指针变量本身的地址取出来了,是一个地址 printf("%d\n", sizeof(&p + 1));// 4 / 8 取出 指针变量 p的 地址,加上 1,是谁的地址,不知道也不重要,因为它还是一个地址 printf("%d\n", sizeof(&p[0] + 1));// 4 / 8, &(*(p+0)) +1 ,a的地址加上一字节,改地址是 b 的地址 printf("%d\n", strlen(p));// 6 指针变量 p 存的是 a的地址,从a开始计算,直到遇到'\0'停止,'\0'不计入在内 printf("%d\n", strlen(p + 1));// 5 指针变量 p 存的是 b 的地址,从b开始计算,直到遇到'\0'停止,'\0'不计入在内 printf("%d\n", strlen(*p));// *p == a == 97,以97为地址( 从 97 开始计算直到遇到'\0' ),属于非法访问,程序崩溃 printf("%d\n", strlen(p[0]));// p[0] == a == 97,以97为地址( 从 97 开始计算直到遇到'\0' ),属于非法访问,程序崩溃 printf("%d\n", strlen(&p));// 随机值, 这里是指针变量 p 的地址,不是a的地址,而 p 后面什么时候能遇到 '\0',我们不知道,所以返回一个 随机值 printf("%d\n", strlen(&p + 1));// 随机值,这里还是取的 指针变量 p 的地址,对齐加一,跳过一个p,意味着 strlen 少计数 一整个 p 所含的元素 printf("%d\n", strlen(&p[0] + 1));// 5 取出第一个元素的地址加一,到第 二 个元素,从第二个元素计算,直到遇到'\0'停止,'\0'不计入在内 return 0; }
二维数组
1. sizeof(数组名) ,此时的 数组名 代表的是 整个数组。
2. &数组名,此时的 数组名 代表的是 整个数组。
除了以上两种特殊情况,其它的,几乎 100% 代表的是首元素地址
另外有一点请记住: arr[ i ] == *(arr + i );
arr[ i ][ j ] == *( *(arr + i)+ j)
arr[ i ][ j ] ,arr[ i ] 就是一维数组的数组名
程序六( 二维数组):
#include<stdio.h> #include<string.h> int main() { int a[3][4] = { 0 }; printf("%d\n", sizeof(a));// 48 此时 数组名 a 代表的是整个数组,意味着 sizeof 计算的是整个数组的大小; 3*4*4 = 12*4 = 48 printf("%d\n", sizeof(a[0][0]));// 4 第一行第一个元素 printf("%d\n", sizeof(a[0]));// 16 把第0行看成一个一维数组,a[0]就是一个一维数组的数组名,所以计算的整个第 0 行元素的内存大小 4 * 4 = 16 printf("%d\n", sizeof(a[0] + 1));// 4 / 8 第0行 第2个元素的地址 printf("%d\n", sizeof(*(a[0] + 1)));// 4 第0行 第2个元素 a[0][1] == *(*(a+0)+1) printf("%d\n", sizeof(a + 1));// 4 / 8 第1行 的 一维数组 的 地址,*(a +1) = arr[1],不就是第1行 一维数组的 数组名,在对其 &(取地址),既然是一个地址,就要受多少位操作系统影响: 4 byte【32位】 / 8byte 【64位】 printf("%d\n", sizeof(*(a + 1)));// 16 解引用 第一行的一维数组的数组名(首元素)的地址,等于就是找到第一行的一维数组的数组名, //sizeof(第一行 的 数组名 ),所以计算的是整个第一行元素的内存大小 printf("%d\n", sizeof(&a[0] + 1));// 4 / 8 第 1 行(第一行的一维数组名)的 地址, printf("%d\n", sizeof(*(&a[0]) + 1));// 16 解引用 第 1 行(第一行的一维数组名)的 地址,等于找到了 第 1 行的一维数组的数组名 //sizeof(第一行 的 数组名 ),所以计算的是整个第一行元素的内存大小 printf("%d\n", sizeof(*a));// 16 a是首元素的地址(第 0 行的数组地址) ,*a(解引用 a )找到了第0行数组名, //计算的是整个第 0 行所有元素的内存大小 printf("%d\n", sizeof(a[3]));//16 ,另外 sizeof()括号里表达式不会计算,也就是说不会真的访问第四行数据,它只是把第四行的数组名放在这里() //意思这里有第四行,就意味着 第四行 有 它自己 的 类型 ,其实这里 a[3] 跟 a[0] 一样的道理, //a[3] 是第四行的数组名,那有 第四行吗?其实没有,但并不妨碍,因为我们不去访问 // 而且 sizeof() 括号里的 表达式 是不参与真实运算的 ,只是 根据 它的类型 计算 它的大小 // 所以 a[3] 是一个一维数组,而且有 4 个整形的一维数组,sizeof(a[3]) == 4 * 4 == 16 return 0; }
指针题
程序七( 指针):
#include<stdio.h> int main() { int a[5] = { 1, 2, 3, 4, 5 }; int* ptr = (int*)(&a + 1);// 数组的指针类型,不能放进一个整形指针里,所以这里强制类型转换,但值没有发生变换, // 存的值,还是跳过一个数组的指向的那个值(未知量) printf("%d,%d\n", *(a + 1), *(ptr - 1));// 整形指针 ptr 减一,指向 5 的 那个位置,再解引用就是5 return 0; // 2 5 }
程序八( 指针):
include<stdio.h> struct test { int num; char* pcname; short s_date; char cha[2]; short s_ba[4]; }* p; 假设 p 的 值为 0x10 00 00 00,如下表达式分别为多少 已知结构体 test 类型 的 变量 大小为 20个字节 #include<stdio.h> int main() { p = (struct test*)0x100000; printf("%p\n", p + 0x1);// 0x1 == 1 这里加1 等于跳过了一个结构体(20字节) //0x10 00 00 + 20 ==0x10 00 14 因为 %p(32位) //打印 0x 00 10 00 14 printf("%p\n", (unsigned long)p + 0x1); // 强制类型转换 无符号长整形类型,0x100000 转换为 10 进制 -> 1048576 + 1 = 1048577 -> 0x10 00 01 // %p, 即0x 00 10 00 01 printf("%p\n", (unsigned int*)p + 0x1);//强制类型转换 无符号整形指针类型 加1 == 就是跳过一个无符号整形 指针(4 byte) // 即 0x 00 10 00 04 return 0; }
程序九( 指针):
#include<stdio.h> int main() { int a[4] = { 1, 2, 3, 4 }; int* ptr1 = (int*)(&a + 1); int* ptr2 = (int*)((int)a + 1); printf("%x,%x", ptr1[-1], *ptr2);// %x 十六进制数 return 0; // 4 2 000 000 } // 1 2 3 4 // 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 // 低地址 高地址(小端 存储:低字节存低地址,高字节存高地址) // a 是数组名(首元素地址),指向 1 所在的地址(01前面)。这时类型强制转换 整形,加1(1字节), // 再将他转换为一个地址,在对其解引用 ,此时它(ptr2)指向 01 的后面,又因为ptr2是int 类型 //也就是说它一次访问 4 个字节的内容( 00 00 00 02 ) 取出来使反着读取(从高位开始读取),02 00 00 00 // 按十六进制 输出 (0x 02 00 00 00) 即,2 000 000
程序十( 指针):
#include<stdio.h> int main() { // 1 3 5 int a[3][2] = { ( 0, 1 ), ( 2, 3 ), ( 4, 5) };// a 是一个 3排 2列 的 二维数组 int* p; //逗号表达式,以最后一个表达式结果为准 // 数组存储情况 1 3 // 5 0 // 0 0 p = a[0]; printf("%d\n",p[0]); // 1 return 0; }
程序十( 图):
#include<stdio.h> int main() { int a[5][5]; // a[0] a[1] a[2] a[3] a[4] // 口口口口口 口口口口口 口口口口口 口口口口口 口口口口口 // | | | | | | | //p[0] p[1] p[2] p[3] p[4] | a[4][2] // p[4][2] int(*p)[4]; p = a;// p的类型 int(*)[4] a的类型 int(*)[5] // p 是一个指向整形数组的指针,有4个元素 printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);// a 先和 [] 集合([]的优先级高) return 0; // p[4] == *(p+4) p[4][2] == *(*(p + 4) + 2) // 指针减指针 等于两个指针之间的元素个数 ,&p[4][2] - &a[4][2] == - 4 } 打印的条件 :%d == - 4 -4 1000 0000 0000 0000 0000 0000 0000 0100 原码(打印) 1111 1111 1111 1111 1111 1111 1111 1100 补码(存储) %p 认为内存上放的是地址,地址就没有原反补码的概念(无符号数) 整形在内存中,以补码类型存储,所以直接以补码当做地址输出 1111 1111 1111 1111 1111 1111 1111 1100 原码符号位不变,其余位按位取反,再加 1 0x f f f f f f f c 所以%p 的输出结果是 ffff fffc
程序十一( 指针):
#include<stdio.h> int main() { int a[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int* ptr1 = (int*)(&a + 1);//跳过整个二维数组 int* ptr2 = (int*)(*(a + 1));// 第一行的数组名 // int aa[5]={0}; // int* p = arr; // *(aa+2) == a[2] == *(p + 2) printf("%d,%d\n",*(ptr1 - 1),*(ptr2 - 1)); return 0;// 10 5 }
程序十二( 指针):
#include<stdio.h> int main() { char* a[] = { "work", "at", "alibaba" };// char *p = "abcdef"; p 存的是字符串首元素(a)的地址 //这里也是一样,字符指针数组 a ,数组名 a == 首元素("work")中 'w ' 的地址 char* *pa = a;// *pa(指针),类型 char* ,*pa指向第一个元素(a == 首元素的地址) // 遗迹指针的地址需要一个二级指针来接收 pa++;//跳过一个char*,指向第二个元素, printf("%s\n",*pa);// at return 0; }
程序十三( 指针):
#include<stdio.h> int main() { char*c[] = { "ENTER", "NEW", "POINT", "FIRST" }; char* *cp[] = { c + 3, c + 2, c + 1, c }; // FIRST POINT NEW ENTER // c[] cp // char* ENTER c+3 char** FIRST -> cpp // char* NEW c+2 char** POINT // char* POINT c+1 char** NEW // char* FIRST c char** ENTER char** *cpp = cp; printf("%s\n",* *++cpp); // POINT 此时cpp指向第二个元素(c+2),再对其解引用 POINT 的首元素地址 此时 cpp 指向 c+2(第二个元素) 处,因为 上面 ++cpp 改变 cpp 的指向 printf("%s\n", *--*++cpp + 3); // ER ++ -- 优先级比 + 高, //所以先加加,此时cpp指向第三个元素,再进行 *(解引用)== 第三个元素(c+1), //再 -- (第三个元素减一),即 c + 1 - 1 == c ,再进行 *(解引用),此时 等于 ENTER (此时类似于数组名) // 最后再加 3 (ENTER + 3),最终指向 "ENTER"中第四个元素 E 的地址 ,然后从 E 开始往后打印,直到遇到'\0'停止, // 即 最后输出为 : ER 注意此时的 char**cp[]={c+3,c+2,c,c};因为 在 *++p,解引用找到第三个元素 c+1之后,前置减减了一波。所以改变了 第三个元素的值 // 另外 注意 此时 cpp是指向第三个元素 c 的。 printf("%s\n", *cpp[-2] + 3); // *cpp[-2] + 3 = *(cpp-2) + 3 ; cpp-2 == cpp[0] ==(c+3) // 再对其解引用,等于 FIRST // 再加上三,此时指向(FIRST 此时类似于数组名),FIRST + 3 第四个元素 S 的地址 // 然后从 S 开始打印,直到遇到'\0'停止。 // 即 最后输出为:ST // 注意 此时 cpp 还是指向第三个元素 c 的(前面 *cpp[-2],并不会改变其值,它只是加上一个数,然后去访问,就好比 一个人 站着凳子 去拿柜子上高处的东西,并不会改变自身身高,而前置和后置 ++,自增,也就是说等自己长高了再去拿,此时的身高已经改变了), printf("%s\n", cpp[-1][-1] + 1); // == *( *(cpp - 1) -1 ) + 1 // *(cpp - 1) == c + 2 // 再 减 1, c + 2 - 1 = c + 1 // 在对其解引用 等于 NEW // 再加 1 ,NEW(此时类似于数组名) +1 ,此时得到了第二个元素(E)的地址 //然后 从 E 这里开始打印,直到遇到'\0'停止。 // 即 最后输出为 EW return 0; }
如果有疑问,欢迎在下面评论。本文至此就全部结束了。
加载全部内容