1. 常指针与常量的指针
char * const p;
char const * p
const char *p
上述三个有什么区别?
char * const p; //p为只读指针。
char const * p;//p值只读的指针。
const char *p; //和char const *p
---------------------------------------------------
2.定义与声明
声明是普通的声明:它所描述的并非自身,而是描述在其他地方创建的对象。
定义是特殊的声明:它为对象分配内存,即现场创建对象。
---------------------------------------------------
3.左值与右值
I.左值:编译时可知。左值可以分为可修改左值和不可修改左值,数组名就是一个不可修改的左值。即不能给数组名赋值!(注意这里指的是“数组名”) 这是为什么呢?下面有一个现场:
char a[10] = {'a'};
char b[10] = {'b'};
a = b;
编译时出现的错误:
error: incompatible types when assigning to type ‘char[10]’ from type ‘char *’
结论:
当数组名为左值时,它的类型是字符数组;当数组名为右值时,它的数据类型是字符指针。
II.右值:运行时可知。
---------------------------------------------------
4.指针与数组
如果编译器需要一个地址来执行一种操作,对于被定义的数组变量而言的,它地址在编译时可知,所以它就可以直接进行操作;而对于被声明的指针而言,只有在程序运行的时候才知道它所指向的地址的值,然后才能在当前地址上操作。
char array[] = “sdfsdf”; … c = a[i];
数组的下标引用步骤:(1)取编译器符号中的符号array的地址(假设是8888)
(2)array[i]即为取地址8888+i的内容。
char *p; … c = *p;
指针的间接引用步骤:(1)取编译器符号表中符号p的地址(假设是7777)
(2)获取7777位置处的内容(假设是9999)
(3)取9999处地址的内容,即为*p
char *p = “abcdefg”;
char a[] =“abcdefg”;
那么,p[i]与a[i]的区别?
a[i]是直接在(符号表a的地址+i)处获取内容,即为直接引用。
p[i]是先获取符号表p地址的内容,然后在该内容上+i地址处获取内容,即为间接引用。这里的“间接”指的是要被操作的地址不能直接从编译器符号表中直接获得,而是从指针对象中获得。
char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char *str5 = "abc";
const char *str6 = "abc";
char *str7 = "abc";
char *str8 = "abc";
cout << ( str1 == str2 ) << endl;
cout << ( str3 == str4 ) << endl;
cout << ( str5 == str6 ) << endl;
cout << ( str7 == str8 ) << endl;
打印结果是什么?
解答:结果是:0 0 1 1
str1,str2,str3,str4是数组名,各表示一个数组变量(即对象变量),它们有各自的内存空间;而str5,str6,str7,str8是指针变量,它们指向相同的常量区域。不同的对象之间当然不可能相等,而指向相同区域的指针变量是相等的。
-----------------------------------------------
5. sizeof()函数
以下代码中的两个sizeof用法有问题吗?
void UpperCase( char str[] ){ // 将 str 中的小写字母转换成大写字母
int i;
for( i = 0; i < sizeof(str)/sizeof(str[0]); ++i ){
if(str[i] >= 'a' && str[i] <= 'z')
str[i] -= ('a'-'A' );
}
}
int main(){
char str[] = "aBcDe";
printf("str字符长度为: %lu/n", sizeof(str)/sizeof(str[0]));
UpperCase( str );
printf("%s/n", str);
}
答:函数内的sizeof有问题。
根据语法,sizeof只能测出定义的对象,不能测出声明的变量。静态数组是定义的对象,可以测出;而在函数内,str只是一个声明的变量,非定义的对象,故不能测出。
在gcc4.4.5/x86_64-Linux-gnu环境下有,
sizeof(char*)=8
sizeof(int*)=8
所以,这里可以得到正确的结果,但是有时候,可能出现数组越界的情况。
-------------------------------------------------
6.C不进行数组 的下标检查,地址可以越界,间接访问的时候可能出现“段错误”:
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d/n",*(a+1),*(ptr-1));
输出结果是什么?
答案:输出:2,5
*(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5
&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)
&a是数组指针,其类型为 int (*)[5];
--------------------------------------------
7.strcpy()函数:
int main(){
char a;
char *str=&a;
strcpy(str,"hello");
printf("%s/n", str);
}
答案:没有为str分配内存空间,将会发生异常。问题出在将一个字符串复制进一个字符变量指针所指地址。虽然可以正确输出结果,但因为越界进行内在读写而导致程序崩溃。
---------------------------------------------
8.指针的间接引用
分析下面代码的问题:
char* s="AAA";
printf("%s",s);
s[0]='B';
printf("%s",s);
分析:对象s的内容,是一个常量字符串“AAA”内存空间的首地址。s[0]='B'这一句,表示给一个字符常量赋值,显然是不合法的。
---------------------------------------------
9. int (*s[10])(int) 表示的是什么?
答案:int (*s[10])(int) 函数指针数组,每个指针指向一个int func(int param)的函数。
---------------------------------------------
10. 传值函数,对主函数中的变量没有影响
void getmemory(char *p){
p=(char *) malloc(100);
strcpy(p,"hello world");
}
int main(){
char *str=NULL;
getmemory(str);
printf("%s",str);
printf("/n");
free(str);
return 0;
}
在getmemory()函数中,参数p充当一个行参。在主函数中,free()操作一个空指针,不会起任何作用。
------------------------------------------
11.要对绝对地址0x100000赋值,我们可以用*((unsigned int*)0x100000) = 1234;
那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?
这里必须要注意:有方法分配指定区域的内存,是给绝对地址赋值的前提条件。纠结的是,C没有提供分配指定区域内存的方法。
答案:*((void (*)( ))0x100000 ) ( );
首先要将0x100000强制转换成函数指针,即:
(void (*)())0x100000
然后再调用它:
*((void (*)())0x100000)();
用typedef可以看得更直观些:
typedef void(*)() voidFuncPtr;
*((voidFuncPtr)0x100000)();
------------------------------------------
12. 分析下面的程序:
void GetMemory(char **p,int num){
*p = malloc(num);
}
int main()
{
char *str=NULL;
GetMemory(&str,100);
strcpy(str,"hello");
free(str);
if(str!=NULL){
printf("%X/n", str);
}
}
问输出结果是什么?
答案:输出的是指针对象str的值。在个指针分配内存的时候,其实给指针对象赋了一个值,这个值就是这片空间的首地址。释放空间后,这个指针对象的值,其实是指向没有被分配空间的地址。访问这样的越界空间,编译是没有问题的,但是运行时编译器出现“Segmentation fault”.
-------------------------------------------
13.分析下面程序的结果:
int main()
{
char a[10];
printf("%d",strlen(a));
}
答案:0,这由strlen()函数的内部实现有关。
--------------------------------------------
14.char (*str)[20];
char *str[20];
---------------------------------------------
15.分析下面代码:
typedef struct AA{
int b1 : 5;
int b2 : 2;
}AA;
void main()
{
AA aa;
printf("%d/n", 'A');
char cc[100];
strcpy(cc,"0123456789abcdefghijklmnopqrstuvwxyz");
memcpy(&aa,cc,sizeof(AA));//将sizeof(AA)个连续的字节空间(从cc开始),源和目的地不能重叠
printf("%d %d/n", aa.b1, aa.b2);
}
首先sizeof(AA)的大小为4,b1和b2分别占5bit和2bit.经过strcpy和memcpy后,aa的4个字节所存放的值是: 0,1,2,3的ASC码,即00110000,00110001,00110010,00110011所以,最后一步:显示的是这4个字节的前5位,和之后的2位分别为:10000,和01,因为int是有正负之分