指针运算
void main()
{
int TestArray[5][5] = { {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}
};
int* p1 = (int*)(&TestArray + 1);
int* p2 = (int*)(*(TestArray + 1) + 6);
printf("Result: %d; %d; %d; %d; %d\n", *(*TestArray), *(*(TestArray + 1)), *(*(TestArray + 3) + 3), p1[-8],
p2[4]);
// 11 , 16 , 29 , 28 , 26
}
解析: 严格上讲,C语言中没有二维数组,二维数组只是是数组的数组。那么,题目中的TestArray指针的类型是 int (*)[5],每个元素都是5个元素的数组。&TestArray代表整个二维数组空间,加1之后指向35后面,因此p1[-8] = 28。
* (TestArray + 1)表示第二个数组的地址,p2指向22。
二维数组的数组名是指向二维数组中第一个一维数组的指针。
二维数组以一维数组的形式保存在内存中。
调试经验
void main()
{
char* p = "hello world!";
int a = (int)p;
short s = 'c';
printf("%c\n", (long)(*((int*)p)));
printf("%s\n", a);
printf("%s\n", &s);
}
输出结果为:h,hello world!, c
问题处在printf函数上,printf函数采用可变参数列表,可变参数列表无法判断输入参数的类型,printf把所有的参数都默认为char* 类型。
(int*)p表示指向内容为hell的4个字节的指针,再加上星号 * , * (int*)p表示“hell”前四个字节的内容,%c表示打印一个字符,打印结果就是h
int a = (int)p;将字符串的首地址赋值给a。打印出整个字符串。
short s = ‘c’;short是2字节,本系统中s的低地址存放‘c’,高地址为0,这样打印出来的字符串就是“c”,printf一直打印到0处。
printf函数如果使用“%s”打印字符串,则需要指向字符串的指针或者字符串的地址。
printf函数如果使用“%d”或“%c”打印数值,则需要int或char等变量的值,但不是地址。
安全编程
#include<stdio.h>
int main(int argc, char *argv[])
{
int flag = 0;
char passwd[10];
memset(passwd,0,sizeof(passwd));
strcpy(passwd, argv[1]);
if(0 == strcmp("LinuxGeek", passwd))
{
flag = 1;
}
if( flag )
{
printf("\n Password cracked \n");
}
else
{
printf("\n Incorrect passwd \n");
}
return 0;
}
在上面的程序中,如果输入的密码超过了10个字符,会把flag变量改写,因为在函数中,局部变量存放在栈空间中,栈空间是从高地址向低地址发展的,flag在高地址,passwd数组在低地址,当输入的字符过多时,会改写flag值,导致程序输出错误。
使用strncpy(passwd, argv[1],10);
可以解决这个问题