目录
1. 示例1
#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;
}
运行结果如下:
分析:
(1)a=&a[0],a+1=&a[0]+1表示跳过1个整型元素后的地址,解引用得到2;
(2)&a得到整个数组的地址,&a+1表示跳过整个数组后的地址;
将&a+1强转为int*类型,命名为ptr,ptr-1表示前移1个整型元素后的地址,解引用得到5;
2. 示例2
#include<stdio.h>
// 假设结构体大小为20B,平台为x86
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p = (struct Test*)0x100000; // p为结构体指针变量,值为0x100000
int main()
{
printf("%p\n", p + 0x1); // struct Test类型变量,+1即跳过20(0x14);
printf("%p\n", (unsigned long)p + 0x1); // 强转为长整型,+1即数值+1;
printf("%p\n", (unsigned int*)p + 0x1); // 强转为int*类型(地址),+1即跳过4(x86)
return 0;
}
运行结果如下(x86):
分析:
(1)struct Test* p=(struct Test*)0x100000表示创建struct Test*类型的结构体指针变量p,并赋值0x100000;(0x100000本身应为int型,故需强转为struct Test*类型)
(2)注意区别数值+1与指针+1,指针+1才与指针类型相关,数值+1就是简单的数值运算;
(3)注意十六进制运算,20D=0x14H;
3. 示例3
#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)关于二维数组的初始化:
逐行初始化时,需使用{ } 而非( ),本例中二维数组的(x, y)被识别为表达式:(x, y)=y,
仅初始化了1、3、5三个元素,其余三个元素被默认初始化为0,故数组内容如下:
(2)对于二维数组a,a[0]即第1行的一维数组的数组名=第1行一维数组的首元素a[0][0],
即p = a[0] = &a[0][0],p[0] = *(p+0) = a[0][0] = 1;
4. 示例4
#include<stdio.h>
int main()
{
int a[5][5];
int(*p)[4]; // p为数组指针类型,p指向4个整型元素的数组
p = a;
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
return 0;
}
运行结果如下(x86):
分析:
(1)a既表示二维数组的数组名,也表示二维数组第1行的一维数组的地址,即a=&a[0];
即a是int (* ) [ 5 ] 类型,指向5个整型元素的数组,a为数组指针类型;
p为数组指针类型,指向4个整型元素的数组,即 int (* ) [ 4 ] 类型;
注:对于p=a,由于类型差异,编译器会报警告:
(2)关于输出的指针-指针,| 指针-指针 | = 指针之间的元素个数;
有关指针运算,详见下文:
【C语言】_指针运算-CSDN博客https://blog.csdn.net/m0_63299495/article/details/144916134(3)内存分布如下:
可见两地址相差4;
(4)对于%p和%d输出,%d用于打印有符号整数,%p用于打印地址(解析为无符号数);
-4D=(1000 0000 0000 0000 0000 0000 0000 0100)原
=(1111 1111 1111 1111 1111 11111 1111 1100)补 = 0xFFFF FFFCH;
5. 示例5
#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;
}
运行结果如下:
分析:
(1)aa=&aa[0],aa+1=&a[0]+1表示跳过一个整型元素,;
&aa得到整个数组的地址,&aa+1表示跳过一整个数组;
(2)对于 int* ptr2 = (int*)(*(aa + 1)) 的理解:
* (aa+1) = * (&a[0]+1 ) = * (&a[1] ) = a[1],
即第2行一维数组的数组名,又可解析为:a[1]=&a[1][0];
(3)内存分布如下:
6. 示例6
#include<stdio.h>
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
运行结果如下:
分析:
(1)对于char* a [ ] = { "work", "at", "alibaba" } 的理解:
① 对于常量字符串,可使用字符指针方式定义,如:
char* p="abcdef"表示p=&('a'),表示:将常量字符串的首字符的地址赋给字符指针变量p;
② char* a[ ] 表示以数组名为a的数组的元素为char*类型,即a为指针数组,
结合①分析可知:a数组中存放分别指向'w', 'a', 'a'的字符指针变量;
关于常量字符串,参考下文:
【C语言】_字符数组与常量字符串-CSDN博客https://blog.csdn.net/m0_63299495/article/details/145016708(2)pa=a=&a[0],pa++即pa=pa+1=&a[0]+1,由于&a[0]为char*类型,+1则跳过一个char*类型元素,故&a[0]+1=&a[1],指向"at"的'a';
(3)关于%s打印:使用%s打印时,从给定地址开始打印字符串,直至'\0';
(4)内存分布如下:
7. 示例7
#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)未进行自增、自减或解引用操作前的内存分布如下:
(2)对于第一行输出:
++cpp使得cpp存放cp+1,第一次解引用得到cp+1;
cp+1指向c+2,第二次解引用得到c+2;
c+2指向'P',%s输出得到POINT,故输出POINT;
(3)对于第二行输出:
首先明确+优先级最低,其次明确自增++的累计作用,即受第一行影响。
++cpp使得cpp存放cp+2,指向c+1,第一次解引用得到c+1;
--(c+1)使得cp+2指向的空间的内容变为c,c指向ENTER,第二次解引用得到ENTER;
此时char*类型指针c指向'E'(ENTER的第一个E),+3则指向'E'(ENTER的第二个E),%s打印ER:
(4)对于第三行输出:
*cpp[-2] +3即 **( cpp-2 ) +3,cpp-2使得cpp存放内容为cp,第一次解引用得到cp;
cp指向c+3,第二次解引用得到c+3;
c+3指向'F',+3使得c+3指向‘S’,%s打印字符串,输出ST;
(5)对于第四行输出:
cpp [-1] [-1] + 1 即 * ( * ( cpp-1 ) - 1) +1,
cpp-1使得cpp存放的内容为cp+1,第一次解引用得到cp+1;
cp+1指向c+2, 再-1使得cp+1指向c+1,第二次解引用得到c+1;
(c+1)指向NEW,再-1使得c+1指向'E',%s输出得到EW,打印EW;
注:注意不要混淆++与+,--与-,自增++自减--会改变该变量的值(输出一、输出二),输出时进行+-操作只会作用于输出结果,不会改变变量的值(输出三、输出四);
标签:面试题,示例,int,C语言,数组,cpp,cp,指针 From: https://blog.csdn.net/m0_63299495/article/details/145113650