关于C语言的15个指针面试题

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是有正负之分

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值