C语言 - 指针题目解析

本文深入解析了C语言中一维数组、字符数组、二维数组等的使用方法及特性,通过具体示例详细说明了数组与指针的关系、sizeof与strlen的区别,并探讨了不同类型数组的操作技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目1

//一维数组

int a[ ] = { 1,2,3,4 };


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

//sizeof(数组名),计算的是整个数组的大小,为4×4=16

printf("%d\n", sizeof(a + 0));

//当操作数不只是数组名时,数组名代表数组首元素的地址,因此a+0同样是数组首元素的地址,地址在32位机占4个字节,在64位机占8个字节,4/8

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

//a是数组首元素地址,*a是数组首元素,类型是int型,占4个字节,4

printf("%d\n", sizeof(a + 1));

//a是数组首元素地址,加1向后偏移一个int型的字节,就是数组第二个元素的地址,仍然是一个地址,因此同样占4/8个字节

printf("%d\n", sizeof(a[1]));

//a[1]也可以化为 *(a+1),是数组第二个元素,为int型,占4个字节,4

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

//&a取出的是整个数组的地址,它与数组首元素的地址是相同的,但两者意义却是不同的,但其仍为地址,同样占4/8个字节

printf("%d\n", sizeof(*&a));

//&a先取出了整个数组的地址(是int (*) [4]类型),解引用符号访问的权限是整个数组,计算的是整个数组的大小,占16个字节

printf("%d\n", sizeof(&a + 1));

//先取出整个数组的地址,再将其加1,这里的加1其实是向后跳过了一个数组的大小,是数组尾元素的下一个地址,仍然是一个地址,占4/8个字节

printf("%d\n", sizeof(&a[0]));

//&arr[0]( 可以化为 &(*(a+0)) ),取出数组首元素的地址,占4/8个字节

printf("%d\n", sizeof(&a[0] + 1));

//&a[0]+1( 可以化为 &(*(a+0))+1),表示数组第二个元素的地址,占4/8个字节

//字符数组


char arr[ ] = { 'a','b','c','d','e','f' };

printf("%d\n", sizeof(arr));

//sizeof(数组名),计算整个数组的大小,为1×6=6

printf("%d\n", sizeof(arr + 0));

//arr+0是数组首元素的地址,占4/8个字节

printf("%d\n", sizeof(*arr));

//arr表示数组首元素地址,*a是数组首元素,类型是char,占1个字节,1

printf("%d\n", sizeof(arr[1]));

//arr[1]是数组第二个元素,类型是char,占1个字节,1

printf("%d\n", sizeof(&arr));

//&arr,取地址+数组名取出的是整个数组的地址,占4/8个字节

printf("%d\n", sizeof(&arr + 1));

//&arr取出整个数组的地址,加1向后跳过了一个数组的大小,是数组尾元素的下一个地址,占4/8个字节

printf("%d\n", sizeof(&arr[0] + 1));

//&arr[0]+1,&arr[0] 取出数组首元素的地址,加1向后移动一个char的字节,是数组第二个元素的地址,占4/8个字节

//strlen计算字符串长度,给定一个指针,从该地址开始查找,依次向后查找1个字节,当找到'\0'时停止

printf("%d\n", strlen(arr));

//arr是数组首元素的地址,从此地址向后找'\0',由于数组的最后一个元素不是'\0',因此当查找尾元素后,将继续查找,直到找到'\0',因此结果应为随机值
 

printf("%d\n", strlen(arr + 0));

//arr+0仍为数组首元素的地址,如上,为随机值


printf("%d\n", strlen(*arr));

//arr是数组首元素地址,*arr是数组首元素,是一个char型元素,字符‘a',但参数想要的是一个地址,因此'a'的ASCII码值97将会被看成是一个地址,这样进行内存访问会出错 err


printf("%d\n", strlen(arr[1]));

//arr[1]是数组第二个元素,'b' - 98,它表示的意义和*arr一样,同样会出错 err


printf("%d\n", strlen(&arr));

//&arr取出整个数组的地址,此地址和数组首元素的地址一样,但意义不同,但在这里仍然是从此地址开始向后找'\0',因此仍为随机值


printf("%d\n", strlen(&arr + 1));

//&arr取出整个数组的地址,加1后向后跳过了一个数组的字节大小,是数组尾元素的下一个地址,从此位置向后找'\0',在如上数组中,结果应为随机值-6


printf("%d\n", strlen(&arr[0] + 1));

//&arr[0]取出数组首元素地址,加1向后移动一个char的大小,是数组第二个元素的地址,从此位置向后找'\0',在此数组中,结果应为随机值-1

char arr[ ] = "abcdef";


printf("%d\n", sizeof(arr));

//sizeof(数组名),计算整个数组的大小,为1×7=7

printf("%d\n", sizeof(arr + 0));

//arr+0是数组首元素的地址,占4/8字节

printf("%d\n", sizeof(*arr));

//*arr是数组首元素,char型,占1字节

printf("%d\n", sizeof(arr[1]));

//arr[1]也是数组首元素,char型,占1字节


printf("%d\n", sizeof(&arr));

//&arr取出的是整个数组的地址,也同样是地址,占4/8个字节


printf("%d\n", sizeof(&arr + 1));

//&arr+1取出整个数组地址,加1后跳过一个数组的大小,是数组尾元素的下一个位置的地址,占4/8个字节


printf("%d\n", sizeof(&arr[0] + 1));

//&arr[0]+1,取出数组首元素地址后加1,是数组第二个元素的地址,占4/8个字节


printf("%d\n", strlen(arr));

//arr是数组首元素的地址,从此位置向后找'\0',结果为6


printf("%d\n", strlen(arr + 0));

//arr+0同样是数组首元素的地址,结果为6


printf("%d\n", strlen(*arr));

//*arr,是数组首元素的值,为'a',ASCII码值被看作一个地址,会出现错误 err


printf("%d\n", strlen(arr[1]));

//arr[1]是数组第二个元素,为'b',同样会出现错误 err


printf("%d\n", strlen(&arr));

//&arr,取出整个数组地址,与数组首元素地址相同,从此位置向后找'\0',结果为6


printf("%d\n", strlen(&arr + 1));

//&arr+1,取出整个数组地址,加1向后跳过一个数组的大小,是数组尾元素的下一个位置的地址,从此向后找'\0',结果为随机值


printf("%d\n", strlen(&arr[0] + 1));

//&arr[0]取出数组首元素地址后加1,是数组第二个元素的地址,从此向后找'\0',结果为5

char* p = "abcdef";

//字符指针指向常量字符串


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

//p是一个指针变量,占4/8个字节


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

//p+1,p指向'a',加1后向后移动一个char的大小,是'b'的地址,占4/8个字节


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

//*p是首元素'a',是char型,占1个字节


printf("%d\n", sizeof(p[0]));

/p[0],同样是首元素'a',占1个字节


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

//&p,是指针变量p的地址,占4/8个字节


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

//&p+1是跳过p之后的地址,占4/8个字节


printf("%d\n", sizeof(&p[0] + 1));

//&p[0]取出'a'的地址,加1是后是‘b'的地址,占4/8个字节

printf("%d\n", strlen(p));

//p是'a'的地址,向后找'\0',结果为6


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

//p+1,p指向'a',加1后指向'b',向后找'\0',结果为5


printf("%d\n", strlen(*p));

//*p,*p是字符'a' - 97,err

printf("%d\n", strlen(p[0]));

//p[0],p[0]同样代表字符'a',err


printf("%d\n", strlen(&p));

//&p取出指针变量p的地址,从此向后找'\0',结果是随机值


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

//&p+1是指针变量p的下一个地址,从此向后找'\0',结果是随机值


printf("%d\n", strlen(&p[0] + 1));

//&p[0]是‘a'的地址,加1后是'b'的地址,向后找'\0',结果是5

//二维数组


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

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

//sizeof(数组名),计算的是整个数组大小,4×3×4 = 48

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

//是数组第一行第一个元素,int型,4个字节


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

//arr[0]是第一行的数组名,sizeof(第一行数组名),计算的是第一行的大小,4×4 = 16

printf("%d\n", sizeof(a[0] + 1));

//a[0]是第一行数组名,没有单独放在sizeof内,也没有被取地址,因此是数组首元素的地址,a[0]+1是数组第一行第二个元素的地址,占4/8个字节


printf("%d\n", sizeof(*(a[0] + 1)));

//是数组第一行第二个元素,int型,占4个字节


printf("%d\n", sizeof(a + 1));

//a是数组第一行的地址,a+1是数组第二行的地址,4/8


printf("%d\n", sizeof(*(a + 1)));

//*(a+1)是对第二行地址解引用,计算的就是数组第二行的大小,4×4 = 16


printf("%d\n", sizeof(&a[0] + 1));

//&a[0]取的是数组第一行的地址,加1就是数组第二行的地址,4/8


printf("%d\n", sizeof(*(&a[0] + 1)));

//*(&a[0] + 1),对第二行的地址解引用,计算的是数组第二行的大小,4×4 = 16


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

//a是数组第一行的地址,对它解引用是第一行,计算的是数组第一行的大小,4×4 = 16


printf("%d\n", sizeof(a[3]));

//a[3]是数组第四行,虽然整个数组只有三行,但是sizeof在计算时实际上是根据数据类型计算的,而这个数组一行的类型是int [4],因此所占字节为4×4 = 16

注:下述题目都以32位机来进行解答

题目二

#include<stdio.h>

int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int* ptr = (int*)(&a + 1);
    printf("%d,%d", *(a + 1), *(ptr - 1));
    return 0;
}

题目三

#include<stdio.h>
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;

int main()
{
    p = (struct Test*)0x100000;

	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0;
}

32位机下 此结构体大小为20个字节

64位机下 此结构体大小为32个字节

p+0x1,此时p是struct Test*型,在32位机下,加1向后偏移20个字节,00100014;在64位机下,加1向后偏移32个字节,0000000000100020

(unsigned long)p+0x1,p被强制类型转换为无符号长整形,加1就是整型加1,32位机,00100001;64位机,0000000000100001

(unsigned int*))p+0x1,p被强制转换为unsigned int*型,加1向后偏移4个字节,32位机,00100004;64位机,0000000000100004

注:结构体大小对于不同环境下可能不同,运行结果可能出现差异

题目四

#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);
    return 0;
}

运行后的值为4,2000000

注:此代码结果在32位机可得,64位机指针变量比整型变量大,无法得到此答案

题目五

#include<stdio.h>
int main()
{
    int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    int* p;
    p = a[0];
    printf("%d", p[0]);
    return 0;
}

逗号表达式,这个数组实际上存储的值为 1,3,5,0,0,0

p是数组第一行首元素的地址,p[0]可化为*(p+0)是第一行首元素,输出1

题目六

#include<stdio.h>
int main()
{
    int a[5][5];
    int(*p)[4];
    p = a;
    printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
    return 0;
}

指针-指针 = 它们之间的元素个数,因此为 -4

以%p打印,认为内存中存放的是地址

-4的补码为 11111111 11111111 11111111 11111100

输出为 FF FF FF FC

题目七

#include<stdio.h>
int main()
{
    int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int* ptr1 = (int*)(&aa + 1);
    int* ptr2 = (int*)(*(aa + 1));
    printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
    return 0;
}

题目八

#include<stdio.h>
int main()
{
	char* a[] = { "work","at","home" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

题目九

#include<stdio.h>
int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;
	printf("%s\n", **++cpp);
	printf("%s\n", *-- * ++cpp + 3);
	printf("%s\n", *cpp[-2] + 3);
	printf("%s\n", cpp[-1][-1] + 1);
	return 0;
}

1.cpp最初指向数组cp首元素地址,++后指向数组qp第一个元素的地址,解引用后是数组cp第一个元素,此元素是char**型。存放着数组c第二个元素的地址,解引用后是数组c第二个元素,此元素是char*型,存放常量字符串"POINT"首字符地址,从此地址向后打印字符串,输出POINT
2.cpp现在指向的是数组cp第一个元素的地址,再次++指向输出cp第二个元素的地址,解引用后是数组cp第二个元素,是char**型,存放数组c第一个元素的地址,--后是数组c首元素的地址,解引用后是数组c首元素,是char*型,存放常量字符串"ENTER"首字符地址,+3向后跳过3个char字节大小,指向字符'e',此从地址向后打印字符串,输出ER
3.cpp[-2]可以化为*(cpp-2),cpp现在指向数组cp第二个元素的地址,-2后是数组cp首元素的地址,解引用后是数组cp首元素,存放的是数组c第三个元素的地址,解引用后是数组c的第三个元素,此元素存放常量字符串"FIRST"首字符地址,+3向后跳过3个char字节大小,指向字符's',从此地址向后打印字符串,输出ST
4.cpp[-1][1]可以化为*(cpp[-1]-1).再化为*(*(cpp-1)-1),cpp指向数组cp第二个元素的地址,-1后指向数组cp第一个元素的地址,解引用后是数组cp第一个元素,此元素存放数组c第二个元素的地址,-1后是数组c第一个元素的地址,解引用后是数组c第一个元素,最后+1向后跳过1个char字节大小,指向'E',从此地址向后打印字符串,输出EW

<think>好的,用户想要查找与C语言指针相关的面试题。我需要根据之前提供的引用内容来整理相关题目解析。首先,看看用户提供的四个引用,都是关于C语言指针的面试题示例,应该可以从中提取出几个典型的题目。 首先,引用1中的题目涉及数组和指针的操作,特别是数组名和指针运算的区别。这题的结果是2和5,需要解释清楚数组名和指针的类型转换后的差异。 然后,引用2的结构体指针题目涉及到指针运算和类型转换。结构体的大小是20字节,所以p+0x1的地址应该是0x100000 + 20,也就是0x100014。而(unsigned long)p + 1则是数值上的加1,结果为0x100001。unsigned int*类型的指针加1,则增加4字节,所以是0x100004。这部分需要详细说明指针运算和类型转换的影响。 引用3的题目是关于指针数组和二级指针的操作。pa初始指向数组a的第一个元素,pa++后指向第二个元素,即"at",所以输出是at。这个相对简单,但需要解释指针数组和二级指针的递增操作。 引用4中的题目涉及到字符串指针和strlen函数的使用。这里有几个易错点,比如解引用指针后的行为是否正确,取地址后的随机值问题。需要逐一分析每个printf的输出结果或错误原因。 接下来,我需要将这些题目整理成用户需要的格式,使用中文,并且确保行内公式和独立公式的正确应用。不过这些题目可能不需要数学公式,但如果有涉及地址计算的地方,可能需要使用行内代码或地址表示,比如使用$...$或代码块。 用户要求生成相关问题,但根据示例,可能是指生成更多相关问题,但根据用户的问题,他们可能希望直接看到已有的题目解析。不过根据用户提供的示例,他们可能希望将现有的引用内容中的题目整理出来,并附上解析,然后生成相关问题。 现在需要按照用户提供的系统级指令来组织回答,包括使用正确的格式,生成相关问题,引用标识等。同时,回答需要结构清晰,帮助用户逐步解决问题。 首先,整理每个引用中的题目,给出解析,然后生成相关问题。例如,引用1的题目解析,引用2的结构体指针问题,引用3的指针数组问题,引用4的strlen问题。然后针对每个题目生成相关问题,如指针运算、类型转换、结构体内存对齐、二级指针操作等。 需要确保每个解析正确,并正确引用来源。例如,在解析每个题目后,添加对应的引用标识,如[^1]、[^2]等。同时,用户可能需要更多的相关问题,以便他们进一步练习,所以需要根据每个题目的知识点提出相关问题。 最后,检查是否符合用户的所有要求,包括中文回答,LaTeX语法正确,行内代码用反引号包裹,引用标识正确添加,结构清晰等。</think>### C语言指针常见面试题解析 #### 1. 数组名与指针运算差异 **题目**: ```c int main() { int a[5] = {1,2,3,4,5}; int* ptr = (int*)(&a + 1); printf("%d %d", *(a + 1), *(ptr - 1)); return 0; } ``` **解析**: - `a`是数组首元素地址,类型为`int*`,`a + 1`指向第二个元素(值为2)。 - `&a`是数组的地址,类型为`int(*)[5]`,`&a + 1`会跳过整个数组(地址增加`5*sizeof(int)`),此时`ptr`指向数组末尾后的位置。 - `ptr - 1`回退一个`int`大小,指向数组最后一个元素(值为5)。 - **结果**:`2 5`。 #### 2. 结构体指针与类型转换 **题目**: ```c struct Test { /* 结构体大小为20字节 */ }* p = (struct Test*)0x100000; int main() { printf("%p\n", p + 0x1); printf("%p\n", (unsigned long)p + 0x1); printf("%p\n", (unsigned int*)p + 0x1); } ``` **解析**: - `p + 0x1`:指针运算按结构体大小(20字节)增加,结果为`0x100014`。 - `(unsigned long)p + 1`:数值运算直接加1,结果为`0x100001`。 - `(unsigned int*)p + 1`:指针类型为`unsigned int*`,加1增加4字节,结果为`0x100004`。 #### 3. 指针数组与二级指针 **题目**: ```c int main() { char* a[] = {"work","at","alibaba"}; char** pa = a; pa++; printf("%s\n", *pa); return 0; } ``` **解析**: - `a`是字符指针数组,`pa`初始指向`a[0]`(即`"work"`)。 - `pa++`后指向`a[1]`(即`"at"`),`*pa`解引用得到字符串`"at"`。 - **结果**:`at`[^3]。 #### 4. 指针与`strlen`函数陷阱 **题目**(简化): ```c char arr[] = "abcdef"; char* p = arr; // 分析以下表达式的行为: strlen(p); // 6 strlen(p + 1); // 5 strlen(*p); // 错误(类型不匹配) strlen(&p); // 随机值(非字符串地址) ``` **解析**: - `strlen(*p)`:`*p`是字符`'a'`(ASCII 97),将97作为地址访问会导致未定义行为。 - `strlen(&p)`:`&p`是`char**`类型,`strlen`会从指针地址开始读取直到遇到`\0`,结果不可预测[^4]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值