C++面试题-指针-指针数组与数组指针

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liubing8609/article/details/86144478

C++面试题-指针-指针数组与数组指针

问:int (*p)[n]int *p[n]的区别?

答:

  1. int (*p)[n]是数组指针,指向某n个元素所组成的整块的数组,返回值是整型指针类型的。
  2. int *p[n]是指针数组,指向数组里面的每个元素,即p[0]指向第一个元素,p[1]指向第二个元素,以此类推,p[n-1]指向第n-1个元素,返回值是整型的。

 

问:下面代码的输出结果是什么?

void main()

{

 int arr[][3] = { 1, 2, 3, 4, 5, 6 };

 int(*ptr)[3] = arr;

 printf("%d %d\n",(*ptr)[0],(*ptr)[1]);

 ptr++;

 printf("%d %d\n", (*ptr)[0], (*ptr)[1]); 

}

答:

1 2 4 5

分析:

int(*ptr)[3] = arr;指向整型数组的指针,数组包含3个元素,也就是说ptr这个指针指向包含了3个元素的一维数组,并且指向arr的首地址。

如果ptr++ptr指向数组的下一个包含3个元素的一维数组。

小结:int (*ptr)[n]指向int a[n]这整块数组。

 

问:下面代码的输出结果是什么?

void main()

{

 int arr[6] = { 1, 2, 3, 4, 5, 6 };

 int(*ptr)[6] = &arr;

 printf("%d,%d,%d,%d,%d,%d\n", (*ptr)[0], (*ptr)[1], (*ptr)[2], (*ptr)[3], (*ptr)[4], (*ptr)[5]);

 ptr++;

 printf("%d %d\n", (*ptr)[0]);

}

答:

1,2,3,4,5,6

-858993460(随机)

分析:

void main()

{

 int arr[6] = { 1, 2, 3, 4, 5, 6 };

 int(*ptr)[6] = &arr; // 这里一定要用取地址符&,因为arrint *类型的,而&arrint (*)[6]类型

 printf("%d,%d,%d,%d,%d,%d\n", (*ptr)[0], (*ptr)[1], (*ptr)[2], (*ptr)[3], (*ptr)[4], (*ptr)[5]);//(*ptr)[0]*(ptr[0])结果相同

 ptr++;

 printf("%d %d\n", (*ptr)[0]); // 不再指向arr数组

}

根据上一试题与这道题结合分析:

int (*ptr)[]是指向由整型数据组成的数组的指针,也就是说ptr指向的是整型元素的数组。

在二维数组当中:

假如int a[2][3]={1,2,3,4,5,6},ptr声明应为int (*ptr)[3]必须为3,因为在二维数组中,拆分为有两个一位数组,即a[2]a[3],这里ptr指向必须是与后面a[3]保持一致,由此可得int (*ptr)[3]ptr指向包含3个元素的一维数组。

注意:由于是二维数组,有a[2],ptr可以自增指向下一个地址,原先ptr指向的是a[0]中包含3个元素的首地址(即&a[0][0])自增之后,ptr就指向了下一个a[1]中包含3个元素的首地址(&a[1][0])

一维数组中:

假如int a[6]={1,2,3,4,5,6}ptr声明应为int (*ptr)[6]必须为6,因为在一维数组中,ptr指向必须与a[6]保持一致.ptr指向包含6个元素的一维数组。

注意:ptr不能自增,因为ptr指针指向了包含6个元素的a数组地址。

ptr是数组指针,指向包含有一个或者一个以上元素的数组的地址。一个ptr对多个元素的数组。

 

问:下面代码输出结果是什么?

int map[2][3] = { { 10, 20, 30 }, { 40, 50, 60 } };

int(*p)[3] =map;

p++;

cout<< **p << endl;

cout<<*p << endl;

cout<< *p + 1 << endl;

cout<< *(*p + 1) << endl;

答:

40

0x0019FF34(内存地址)

0x0019FF38(内存地址)

50

分析:

int map[2][3] = { { 10, 20, 30 }, { 40, 50, 60 } };

int(*p)[3] =map;//如果int (*p)[3]=&map[1];那下一句的p++就可以省略掉

p++;//指向map[1][0]~map[1][2]的数组段的地址

cout<< **p << endl;//map[1][0]

cout<<*p << endl;//map[1][0]地址

cout<< *p + 1 << endl;//map[1][1]地址

cout<< *(*p + 1) << endl;// 因为前面p++,所以一直指向第二个数组段,这里指第二个数组段第二个元素

 

问:求下面代码的输出结果?

int(*p)[4];

int a[4] = { 5, 66, 123, 589 };

p = &a;

cout << p << endl; //注意,p不等于p[0]

cout << "p[0]a的地址:" << p[0] << "  a的地址:" << &a << endl;

cout << "a[0]的内容:" << *(p[0]) << endl;

cout << "a[1]的内容:" << *(p[0] + 1) << endl;

答:

0027F740

p[0]a的地址:0027F740  a的地址:0027F740

a[0]的内容:5

a[1]的内容:66

 

问:下面代码的输出结果是什么?

void main()

{

 int arr[3] = { 1, 2, 3 };

 int* ptr[3];

 ptr[0]= &arr[0];

 ptr[1] = &arr[1];

 ptr[2] = &arr[2];

 printf("%d,%d,%d\n", *ptr[0], *ptr[1], *ptr[2]);

 ptr++;//正确吗?

}

答:

1 2 3

分析:

void main()

{

 int arr[3] = { 1, 2, 3 };

 int* ptr[3];

 ptr[0]= &arr[0];

 ptr[1] = &arr[1];

 ptr[2] = &arr[2];

 printf("%d,%d,%d\n", *ptr[0], *ptr[1], *ptr[2]);//*ptr[0]*(ptr[0])结果相同

 //ptr++;错误,不能自增

}

int* ptr[3]:由返回整型数据的指针所组成的数组 ,是指针数组,有3个成员,每个成员都是一个指针,共有3个指针,返回类型为int*

ptr++错误,因为ptr是指针数组,有指向对象个数的限定,ptr++,就不知道指向哪个内存块了,反正不是数组arr[3]的内存块。

int *ptr[n]int (*ptr)[n]区别:

第一点:

int (*ptr)[n]声明分析:ptr先与*结合,再与外面()结合,ptr是一个指针,又与[n]结合,ptr是指向数组的指针,指向的返回类型为int,所以ptr是一个指向整型数组的指针,即数组指针。

int * ptr[n]声明分析:ptr先与[n]结合,ptr是一个数组,数组返回类型为int*,意思是ptr是一个由整型指针的数组组成,数组里面的每一个元素都是整型指针,即指针数组。

第二点:

ptr[0]对应一个a[0]的地址,ptr[1]对应一个a[1]的地址,ptr[2]对应一个a[2]的地址。

int *a[n]用在一维数组的方法与直接变量赋值相同,例如:

 int *a[3];

 int m = 8, n = 4, x = 3;

 //a = &m;//错误

 a[0] = &m;

 a[1] = &n;

 a[2] = &x;

 

问:请输出下面代码的结果?

 int *a[3];

 int m[2][3] = { 9, 88, 55, 22, 44, 66};

 //a = &m;错误 

 a[0] = m[1];

 cout<<a<<endl; 

 cout<<a[0]<< endl;

 cout<<*a[0]<<endl; 

 cout<<*(a[0]+1)<<endl;

答:

0x0019FF34

0x0019FF28

22

44

分析:

 int *a[3];

 int m[2][3] = { 9, 88, 55, 22, 44, 66};

 //a = &m;错误 

 a[0] = m[1];//指定m[1]的一维数组 

 cout<<a<<endl; 

 cout<<a[0]<< endl;//a[0]是含有三个元素的m[1]的地址

 cout<<*a[0]<<endl; //*a[0]是含有三个元素的m[1]m[1][0]首地址的内容

 cout<<*(a[0]+1)<<endl;//*(a[0]+1)是含有三个元素的m[1]m[1][1]的值

 //cout<<"a+1a[1],指针没有指向的地址:"<<**(a+1)<<endl;//出错 

 

问:下面代码的输出结果是什么? 

#include<stdio.h>

void main()

{

 int arr[2][3] = { 1, 2, 3 ,4,5,6};

 int* ptr[1];

 ptr[0]= arr[1];

 printf("%d,%d\n", *ptr[0],ptr[0][2]);

答:

4 6

分析:

void main()

{

 int arr[2][3] = { 1, 2, 3 ,4,5,6};

 int* ptr[1];

 ptr[0]= arr[1];//arr[1]首地址赋值给ptr[0]指针 

 printf("%d,%d\n", *ptr[0],ptr[0][2]);//ptr[0][2]arr[1]中的第三个元素

}

int *ptr[n]的用法其实和int *ptr差不多。

 

问:下面代码的各个值的输出结果是什么?

void main(){

 int** a;

 int *b[1];

 a = new int*[2];

 int m[3] = { 22, 33, 44 };

 int n[4] = { 4, 5, 6, 7 };

 a[0] = m; 

 a[1] = n; 

 b[0] = m;

 for (int i = 0; i < 3; i++)

 {

  cout << " a[0][i]m[3]数组的第i个元素:" << a[0][i] 

   << "   a[0][i]等价的是*(b[0] + i):" << *(b[0] + i) 

   << endl << endl; 

 }

}

答:

22 22 22

33 33 33

44 44 44

分析:

void main(){

 int** a;

 int *b[1];

 a = new int*[2];//动态二维数组

 int m[3] = { 22, 33, 44 };

 int n[4] = { 4, 5, 6, 7 };

 a[0] = m; // 等价于*a = m;  

 a[1] = n; // 等价于*(a+1) = n;  

 b[0] = m;// b[0]a[0]运用效果一样的,但是类型不一样。b[0]int *b[0],a[0]int **a. 

 for (int i = 0; i < 3; i++)

 {

  cout << " a[0][i]m[3]数组的第i个元素:" << a[0][i] 

   << "   a[0][i]等价的是*(b[0] + i):" << *(b[0] + i) 

   << endl << endl; 

 }

}

该题的代码是动态二维数组,就是在堆上开辟一块内存,然后赋值给二级指针,使用的方法类似于int *a[n]

 

问:数组与指针的区别?

答:数组是多个元素的集合,在内存中分布在地址相连的单元中,所以通过其下标访问不同单元的元素。指针是一种变量,只不过它的内存单元中保存的是一个标识其他位置的地址。由于地址也是整数,在32位平台下,指针默认为32位。

 

问:说说char a1[]=hello”和char *a2="hello"的区别?

答:

a1是一个数组,定义时开辟了数据内存,把常量“hello”赋值给a1(即拷贝),所以常量占有一块内存,而a1又开辟了一块相等的内存。

a2是一个字符类型的指针,指向一个常量"hello"的地址,仅仅只有常量占有内存,而指针本身占有内存为4个字节。(在32位机器中)

 

问:下面的输出结果是?

void *fun(char *arr)

{

char *arr2 ="world";

arr = arr2;

return arr;

}

int main()

{

char *ch = "hello";

fun(ch);

cout << ch << endl;

return 0;

}

答:

hello

分析:这道题主要考指针作为形参的知识点,考时容易出错。由于arr是形参,属于局部变量,作用只在fun函数里面,调用函数完了之后,arr就被释放掉了。实参ch指向仍然不会改变指向。

 

问:下面哪个语句不合法?

char a[10] = {'a'};

 char b[10] = { 'b' };

 a= b;

答:a=b;语句不合法。因为这里数组名ab是字符指针类型的(char*),a作为左值时,是不可修改的值。

分析:当数组名为左值时,它的类型是字符数组;当数组名为右值时,它的数据类型是字符指针。

 

问:下面代码的输出结果是什么?

int pstr(char str[])

{

 return (int)(sizeof(str)-1);

}

void main()

 char a[20] = "Hello world";

 printf("%d",pstr(a));

}

答:

3

分析:当用函数传递数组指针的时候就自动退化为指针了,而指针的长度是4,你减去1自然就是3了。

修改如下:

void main()

 char a[] = "Hello world";

 int len = sizeof(a)-1;

 printf("%d",len);

}

 

问:下面代码输出结果是什么?

void main(void)

{

 char *b = "hello";

 cout << sizeof(b) << endl;

 cout << sizeof(*b) << endl;

}

答:

4 1

分析:sizeof(b) bchar*类型的,输出的结果是指针类型的长度。而sizeof(*b) 中的*bchar类型的,长度为1.

cout<<sizeof(char)<<endl;结果是1

cout<<sizeof(int)<<endl;结果是4

cout<<sizeof(unsigned int)<<endl;结果是4

cout<<sizeof(long int)<<endl;结果是4

cout<<sizeof(short int)<<endl;结果是2

cout<<sizeof(float)<<endl;结果是4

cout<<sizeof(double)<<endl;结果是8

 

问:下面语句的输出结果是什么?

char ch[]="Hello";

char *p=&ch[0];

cout<<p;

答:

Hello

分析:因为数组第一个元素(即ch数组中序号为0的元素)的地址就是数组的首地址,因此,使用指针指向数组第一个元素的地址就是指向数组的首地址。当输出的时候,指针指向的数组元素都输出来了。不过有读者会有疑问,为什么p直接输出数组的全部内容了,而不是数组的首地址呢。详细分析,请看下一道的...下一道题目的分析。

 

问:说说为什么下面代码中“char *p2=&ch;”和“char *p2”是错误的?

char ch[]="hello";

char *p1=ch;

char *p2=&ch;

char *ch2="hello";

char *p3=ch2;

char *p4=&ch2;

答:

char ch[]="hello";

char *p1=ch;//正确

char *p2=&ch;//错误

因为&取地址符的chchar(*)[6]类型的,也就是数组指针类型,意思是一个指向整块数组的指针。所以指针p2指向的变量的数据类型不一致。因此需要强制转换才能通过,即char *p

char *ch2="hello";

char *p3=ch2;//正确

char *p4=&ch2;//编译通过,但输出结果错误

因为&取地址符的ch2char **类型的,也就是二级指针。p4指针指向的变量的数据类型不一致。但是,如果强制转换的话,输出的结果和第一个是不一样的,会出现乱码。

&ch&ch2强制转换成char*类型的区别:

由于ch本身是一个字符数组,取地址符并强制转换之后,指针仍然指向该数组ch的首地址。而&ch2是一个二级指针,强制转换之后,指向的是ch2本身的地址,而不是“hello”这个常量的地址。实践时,把char类型换成int型就容易理解了。

 

问:下面代码有错误吗?

char ch[]="hello";

char *p=ch;

ch[4]=p[0];

printf("%c\n",ch[4]);

printf("%s\n",ch);

答:没有出现错误。

char ch[]="hello";

char *p=ch;

ch[4]=p[0];//p[0]相当于p+sizeof(char)*0

printf("%c\n",ch[4]);//结果:h

printf("%s\n",ch);//hellh

p++;

printf("%s\n",p);//结果:ellh

 

问:下面代码的输出结果是什么?

void main(){

char*ch="Hello"; // 与上一题char[]="Hello"一样的,即假如ch="Hello"=0x1001(0x1001ch数组首地址)

char *p =ch;//也可以直接char *p="Hello";

char *p0 = &ch[0];

char *p1 = &ch[1];

// 输出指定的地址对应的内容+指向地址后的几个字符

 cout << "p"<<p<<endl;

 cout << "p0"<<p0 << endl;

 cout << "p1"<<p1<<endl;

//单个字符的输出

 cout << "*p"<<*p << endl;

 cout << "*p0"<<*p0 << endl;

 cout <<"*p1" <<*p1 << endl;

}

答:输出结果:

pHello

p0Hello

p1ello

*pH

*p0H

*p1e

分析:

由于在C语言中没有真正的字符串类型的,但是正是因为数组元素的地址是连续的,所以可以通过字符数组来表示字符串。由于字符串常量的本质是一个地址,编译器就会为该字符串常量分配地址,比如说分配ch数组的内容“Hello”地址,依次是H0x1001e0x1002l0x1003l0x1004o0x1005/00x1006

而指针变量保存的是地址,它保存该数组ch的首地址,当指针输出结果时候,输出来的字符串当作是地址一样显示出来了,也就是说指针指向的字符数组哪一段地址,就会把这地址后面的内容全部显示出来,假如p1=&ch[1],就会把ch数组中的第2个元素的地址以及以后的地址都显示出来。

 

问:下面代码输出结果是?

void main()

{

char a[]="hello";

char *s=a;

s[0]='B';//正确

printf("%s\n",a);

char a2[]="world";

char *s2=a2;

a2[2]=s2[0];//"r"改变为"w"

printf("a2的第三个字符:%c\n",a2[2]);

printf("%s\n",s2);

}

答:输出结果:

Bello

a2的第三个字符:w

wowld

 

问:下面代码哪条语句是错误的?

char ah[]="hello";

char *aw="world";

char *sh=NULL;

ah=sh;

ah[2]=aw[2];

char *sw=NULL;

aw=sw;

printf("%s",aw);

答:

char ah[]="hello";

char *aw="world";

char *sh=NULL;

ah=sh;//错误,不能把指针赋值给数组ah。因为ah本身是一个数组,数组一旦创建之后,ch一定是该数组的首地址,不能是其他的地址,所以不能把sh这个指针指向的地址赋值给ch

ah[2]=aw[2];

赋值给a[2]a[2]的值会改变。

char *sw=NULL;

aw=sw;

printf("%s",aw);//结果:(null)

 

问:下面代码的输出结果?

void main()

{

char a[]="hello";

char *p="B";

a[2]=*p;

printf("%s\n",a);

char *b="world";

b+=2;

a[0]=*b;

printf("%s\n",a);

}

答:

heBlo

reBlo

分析:

void main()

{

char a[]="hello";

char *p="B";

a[2]=*p;//正确

printf("%s\n",a);

char *b="world";

b+=2;//b指向的常量中的第3个字符地址。

a[0]=*b;//正确

printf("%s\n",a);

}

 

问:下面代码的输出结果是?

char a[]="hello";

char *s=a;

for (int i = 0;i<strlen(a);i++)

{

  printf("%c", s[i]);

 printf("%c", *s++);

}

 printf("%s\n",s);

答:

char a[]="hello";

char *s=a;

for (int i = 0;i<strlen(a);i++)

{

  printf("%c\n", s[i]);//输出的结果是H  e  l  l  o

  printf("%c\n", *s++);//输出的结果是H  e  l  l  o

}

 printf("%s\n",s);//输出同样的结果:He110

 //注意:printf("%s", *s);执行程序会崩溃,因为%s输出的是字符串,而不是单字符

 

问:为什下面代码中,a可以赋值给s?

char* a[]={"heng","yang","shi","fan"};

char **s=a;

答:可以。

由于数组a的首地址可以赋值给s,而且数组a的首地址的内容也可以是另外字符串的地址,即a数组的首地址是“Hello”而“Hello”首地址是“H”。

分析:

char* a[] = { "heng", "yang", "shi", "fan" };//其实a指针数组可以看成**a就容易理解了

char **s = a;

printf("%s\n", *s);///输出“Heng”是字符串

printf("%c\n", **s);//注意%c,输出“H”是一个字符

printf("%s\n", *a);//输出“Heng”是字符串

printf("%c\n", **(a + 1));// 输出“y”是一个字符

printf("%c\n", *(*a+1));//注意%c,输出“e”是一个字符

对于char**s="Hello world";是错误的,因为schar**类型,而“Hello world”是char*类型。

 

问:下面代码的输出结果是 什么?

void main(){

 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

分析:

这道题主要考察了笔者对指针的理解。

由于str1str2这两个字符数组的首地址是不同的,所以等号为假,即结果是0

由于str3str4这两个常量字符数组的首地址是不同的,所以等号为假,即结果是0

由于str5str6这两个指针都指向字符串“常量”的地址,所以这两个指针保存的地址是相同的。

由于str7str8这两个常量指针都指向字符串“常量”的地址,所以这两个常量指针保存的地址相同。

 

问:下面代码的输出结果是什么?

void func(char**p);

void main()

{

 char *arr[] = {"ab","ce","ef","gh","ij","kl"};

 func(arr);

}

void func(char **p)

{

 char *t;

 t = (p += 3)[-1];

 printf("%s",t);

}

A.ab   B.cdC.ef   D.gh

答:C

分析:

p+=3p=p+3,指向a[3],然后又移到上一个元素a[2].具体分析,在以上那几道题目。

 

问:请问以下代码有什么问题?

#inlcude<string.h>

int main()

{

 char a;

 char *str = &a;

 strcpy(str, "hello");

 printf(str);

 return 0;}

答:

书上答案,在gcc或者VC6.0中:

int main(){ char a[20]; char *str = &a; strcpy(str, "hello"); printf(str); return 0;}

VS环境中实践得到的答案:

int main(){ char a[20]; char *str = a; strcpy_s(str, 20,"hello"); printf(str); return 0;}

分析:

由于str并没有分配内存空间,会发生异常。问题出在一个吻字符串赋值进一个字符变量指针所指地址。虽然可以正确输出结果,但因为越界进行内存读写而导致程序崩溃。

 

问:写出下面代码的输出结果?

#include<stdio.h>

int main()

{

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

 int *p = arr;

 printf("%d ",arr[2]);

 printf("%d ", *(arr+2));

 printf("%d ",p[2]);

 printf("%d ",*(p+2));

 *(p + 2) += 3;

 printf("%d ", *(p+2));

 return 0; 

}

答:

输出结果:

3 3 3 3 6

分析:

#include<stdio.h>

int main()

{

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

 int *p = arr;

 printf("%d ", arr[2]);

 printf("%d ", *(arr + 2));

 printf("%d ", p[2]);//作用和arr[2]一样,p[]数组与aa[]用法相似

 printf("%d ", *(p + 2));//

*(p + 2) += 3;//相当于*(p+2)=*(p+2)+3,即*(p+2)=3+3

 printf("%d ", *(p + 2));//只是把*(p+2)当成变量来输出结果

 p++;

 printf("%d ", *p);//这里的结果是2,是arr[1].p指向下一个元素

第一,其实当指针作为运算时,把arr当作p指针处理不难理解。例如*(arr+2)*(p+2)的作用是一样的,虽然数组与指针的本质不相同。

第二,p++是指向下一个元素的地址,p改变成下一个元素的指针。而当p+2是指向下第二个元素的地址,与arr+2分析相同,但是p指向还是第一个元素。即p++p=p+1,而p+2始终不能改变p指向,除非p=p+2

*(p+i) = p[i]= arr[i]=*arr+i

注意:p[]a[]

return 0;

}

 

问:下面代码的输出结果是()

main()

{

char *p;

char a[10]={1,2,3,4,5,6,7,8,9};

p=&(a+1)[3];

printf("%d",*p);

}

A.2  B.3C.4  D.5

答:D

分析:

在上一题目可知,在这道题中,a+1是数组首元素的下一个元素的地址,a+1可看成是指针(本质上与指针有区别),(a+1)[3]=(a+1)+3所以(a+1)[3]输出的是数组a中第五个元素,即a[4]

 

问:下面代码的输出结果是什么?

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

int *ptr=arr;

*(ptr++)+=10;

printf("%d  %d ",*ptr,*(++ptr));

答:

3  3

分析:本题考察了ptr++++ptr的区别。ptr++:先输出ptr,再自增。而++ptr:先自增,再输出ptr

*(ptr++)+=10;语句很容易分析错误,更不能拆分为*(ptr++)=*(ptr++)+10来分析,因为它们两之间还是有区别的。

*(ptr++)+=10分析:

第一步,由于优先级()高于*,所以ptr++提取出来,其他先不管。

第二步,ptr++首先输出的是还没有自增的ptr,所以就会有*ptr+=10,这时还没有自增的ptr指向arr首地址,所以*ptr就是arr[0]的值为11,然后ptr才自增,这时ptr指向arr数组的下一个元素,即arr[1]

*(ptr++)=*(ptr++)+10分析:

第一步,由于优先级()高于*,所以ptr++提取出来,其他先不管。

第二步,ptr++首先输出的是还没有自增的ptr,所以就会有*ptr=*ptr+10,这时还没有自增的ptr指向arr首地址,所以*ptr就是arr[0]的值为11,然后ptr自增两遍,即左右值都要自增,得到的最终结果ptr指向arr[2].

printf("%d  %d ",*ptr,*(++ptr));也容易出错。

输出的规则是从右到左,所以*ptr是执行*(++ptr)之后输出的。

 

问:设有“int w[3][4];, p表示指向数组w的数组指针,则p的初始化语句是什么?

答:

int *p=w;

分析:指针初始化有两种方式:

1.int *p;

p=w;

2.int *p=w;

第一种方式是通过两条语句通过的,而第二种只通过一条语句完成,即初始化语句。所以选择第二种。

 

问:下面代码是否正确?

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

int *p=a;

int *p=a[1];

cout<<*p<<endl;

答:

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

 int *p=a;//错误,数据类型不一致

int *p=a[1];///指向a[1],也就是a[1][0],a[1][2],a[1][3]的合段,&a[1]是不合法的

cout<<*p<<endl;//结果是4,输出a[1][0],默认数组首地址的内容 

 

问:函数中的void和指针中的void*有什么区别?

答:在函数中的void是表示无类型或无返回类型。void指针是通用指针,用来存放任何数据类型的引用。

分析:

void真正发挥的作用在于:

  1. 对函数返回的限定。
  2. 对函数参数的限定。

<函数返回值> <函数名>(参数1,参数2,参数3.......){内容;}

int sum(int a,int b)

{

int c

return c;

}

其中第一个int是返回值 就是别的函数调用此函数时这个函数给他的一个值。

1.如果调用时不需要返回值,则函数写为void sum(int aint b){....},此时函数没有返回值。

如果不需要参数,int sum(void){...}《或者int sum(){...}》,此时void的意义为空,就是没有参数的意思。

3.如果都不要,则为void sumvoid);《或者void sum();

4.

main()

{

return 0;//需要返回值,而且为int型。是因为该函数隐含是返回的是整型类型的。但是在c++上安全检查严格不允许没有int,但是在vc6.0当中,编译器是会通过的。 

void的用:

  1. 对函数返回的限定,当函数不需要返回值时,用void限定,如void fun();
  2. 对函数的参数的限定,当函数不需要接受参数时,用void限定,如int fun(void)

void指针的作用:

1.函数返回值是任意类型的指针,如void *fun();

2.定义函数指针pfun,void(*pfun)(),如果该函数指针指向这类函数(即void函数),例如:

void fun()//这类函数

{

}

 void main()

{

void(*pfun)();

pfun = fun; //指向某个函数

pfun();//调用方法1

(*pfun)(); //调用方法2

}

3.void指针不能复引用,也就是不能取得它指向的地址的内容。

void *pvoid;

int *pint;

printf("%d",*pint);//正确

printf("%d", *pvoid);//错误

由于pint是整型变量指针,解引用取得该指向地址的内容是整型的,知道从第一字节到第四个字节的内存,而且从低到高保存整数的32补码。

pvoid是指向还不知道数据类型的地址的通用指针,复引用取得的内容不清楚是什么数据类型,内容占用的内存多大都不清楚。

扩展知识:double数据类型复引用是从第一字节到第八字节的一块内存,从低到高保存double数的浮点数符号位、阶符、阶码和尾数。

 

问:下面代码中哪个地方是错误的?

#include<iostream>

using namespace std;

void main()

{

 void *pvoid=NULL;

 int *pint;

 int m = 2;

 pint = &m;

 cout << pint << endl;

 cout << pvoid << endl;

 pvoid = pint;

 pint = pvoid;

 cout << pint << endl;

 cout << pvoid << endl;

答:

#include<iostream>

using namespace std;

void main()

{

 void *pvoid=NULL;

 int *pint;

 int m = 2;

 pint = &m;

 cout << pint << endl;

 cout << pvoid << endl;

 pvoid = pint;

 pint = (int *)pvoid;//pint=pvoid错误,赋值给pintpvoid需要强制转换成int类型的。

 cout << pint << endl;

 cout << pvoid << endl;

 

问:关于通用指针与解引用的解决问题,以下代码哪些语句是错误的?

void main()

{

int i=100;

void *p=&i;

*p=0;

//----

int a=100;

void *p2=&a;

*(int*)p2=0;

cout<<a<<endl;

}

答:由于通用指针可以存放任意数据类型的地址,而编译器无法确定该指针指向内存地址中的原始数据类型,因此通用指针无法解引用。

void main()

{

int i=100;

void *p=&i;

*p=0;//错误,通用指针不能解引用

//----

int a=100;

void *p2=&a;

*(int*)p2=0;//正确,由于通过强制转换来指定数据类型,就可以实现解引用

cout<<a<<endl;

}

 

问:引用与指针有什么区别?

答:

  1. 引用必须初始化,指针可以不用。
  2. 引用初始化以后就不能被改变(即只能初始化一次,始终只指向指定的对象),而指针可以改变所指向的对象。
  3. 不存在指向空值的引用,而指针可以指向一个空值,即空指针。
  4. 因为引用不能指向空值,这意味着使用引用之前不需要测试其合法性;而指针则需要经常进行测试。所以使用引用的代码效率要比使用指针的效率高,同时也使引用具有更高的安全性。
  5. 引用不占有内存,而指针是占内存的。
  6. 引用是单个变量的别名,而指针是个实体。
  7. sizeof+引用:引用指向的对象的内存大小。sizeof+指针变量:指针本身的内存大小。
  8. 引用不存在引用常量,指针存在指针常量。

分析:

void main()

{

 int var = 2;

 int &reference;//错误,引用不能没有初始化。

 int *p;//正确,指针可以不初始化,但是隐含安全性问题,比如由于使用该指针时,它没有指向某个对象,会造成野指针。

 int &reference = var;//正确,指向一个对象,即变量。

 int &reference = 0;//错误,不能指向空值

 int &reference = 2;//错误,引用不能指向常量

 //////////////////////////////////////////////////////////////

 int *p = 0;//正确,指针可以指向空值

 int &reference = *p;//不报错,但是隐含非法,因为p指针是个野指针,当输出p的值(没有指向对象的值)就出错

 int &reference = p;//错误,int *类型不能初始化int &类型

 int &reference = (int &)p;//强转化类型,与int *无关。

 cout << &reference << endl;//&reference 是引用指向变量的地址

 cout << reference << endl;//reference 是引用指向变量的值

const int &a//是正确的,常引用,表示不能通过常引用来该变指向目标变量的值

int const &a//是错误的,引用常量,由于引用本身一旦初始化就不能被改变,并不需要const来声明。

}

 

问:下面的代码是否正确?

void main()

{

int b=123;

const int &a=b;

b=555;

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

a=666;

}

答:

void main()

{

int b=123;

const int &a=b;//常引用,即引用指向目标变量的值

b=555;

printf("%d\n",a);//输出结果是555

a=666;//编译错误,不能通过常引用来改变b的值

}

 

问:下面代码是否正确?若不正确,请问哪条语句是错的?

void main()

{

int &a=100;

const int &b=100;

}

答:

void main()

{

int &a=100;//错误

const int &b=100;//正确

const int &b=100//引用指向的是常量,代表的是引用b指向一个const int类型,这个int型的值是不能被改变的。

int &a=100//100是个立即数,由于立即数不是C语言的常量,立即数本身存在于代码段,并不是一个对象或变量,所以引用不能指向它的。

}

 

问:下面代码哪条语句是错误的?

void main()

{

const int ci=1024;

const int &rl=ci;

rl=42;

int &r2=ci;

const int &r3=ci;

const int &r4=r3*2;

}

答:

void main()

{

const int ci=1024;

const int &rl=ci;//正确,引用以其对应的对象都是常量

rl=42;//错误,rl是对常量的引用

int &r2=ci;//错误,试图让一个非常引用指向一个常量对象

const int &r3=ci;//正确

const int &r4=r3*2;//正确

//1.常量对象不能被非常引用指向

//2.防止通过常引用来改变目标变量(非常量)的值

}

 

问:引用的优势?

答:

  1. 引用作为函数参数传递时,实际传递的是实参,可直接对实参进行改变。而指针作为函数参数传递时,传递的时实参的地址。根据引用实参的地址来对其进行操作。不管是引用作为函数参数还是其他地方,由于引用是不占用内存并且无须像指针那样还要寻址,节约时间和空间。
  2. 由于引用一旦定义就必须初始化,还有常引用可以保护指向的目标变量的值不轻易被修改,运用比指针安全。

 

问:设void f1(int *m,long &n);int a;long b;则以下调用合法的是()。

A.f1(a,b)  B.f1(&a,b)

C.f1(a,&b)D.f1(&a,&b)

答:B

分析:指针类型的函数参数的作用:将一个变量的地址传送到函数中,调用时,要求实参是一个地址,所以上面的函数,实参前面加取地址符。也可以这样理解:int *m=&a;

引用作为函数参数的作用:形参是变量名的别名,传递数据的作用。调用时,要求实参是一个变量名,所以上面的函数,实参直接使用变量名。也可以这样理解:long &n=b;

 

问:什么是指针?

答:指针是用来存储内存地址的变量,它指向单个对象的地址,除了void指针类型之外,指针的数据类型与所指向地址的变量数据类型须保持一致。不论指针指向的数据类型是哪一种,他本身永远是整型,保存的是地址。

分析:

int *ip;

const intt *ip2;

ipip2都是指针变量名,int表示该指针变量的类型是整型。*表示指针变量。

 

问:如何初始化指针并对其赋值?

答:指针的初始化就是给指针赋初值,&符号可以用来获取对象的内存地址,并且赋值给指针变量。指针变量的初始化和赋值都可以通过运算符”=“来实现。

分析:指针可以初始化为0(NULL),,没有初始化的指针指向是随机的,它可能导致随机修改了程序的值。

变量的数据类型和指针变量的数据类型要保持一致。所以以下代码是错误的:

int a=123;

long *p;

p=&a;

 

问:是否可以确定指针指向一个对象?

答:指针用于指向对象,一个指针只指向一个对象的内存地址。

 

问:指针和迭代器主要的区别?

答:指针和迭代器都是提供其所指对象的间接访问。区别是:指针用于指向单个对象,而迭代器只用于访问容器内的元素。

 

问:运用好指针有哪些优点?

答:

  1. 提高程序的编译效率和执行速度。
  2. 通过指针可使用主调函数和被调函数之间共享变量或数据结构,便于实现双向数据通讯。
  3. 可以实现动态的存储分配。
  4. 便于表示各种数据结构,编写高质量的程序。
  5. 使表达式变得紧凑和简洁。

 

问:使用指针不恰当的会出现哪些问题?举些例子。

答:

  1. 访问数组和其他数据结构时越界。
  2. 自动变量消失后被引用。
  3. 堆上分配的内存释放后被引用。
  4. 内存分配之前解引用。

 

问:指针是一种特殊的变量,只能用来保存地址。这句话对么?

答:对的。

 

问:一个32位的机器,该机器的指针是多少位?

答:4位。指针变量的位数根据机器地址总线位数而定,对于32位地址总线的机器指针的位数就是4个字节。

分析:系统为指针变量分配一定的内存空间,无论指针变量指向何种类型的数据,指针变量的长度一般是一个机器的字长。所以,在32位的机器就是4,但是其他位数的机器就不一定是这个结果了。(注意:分配的内存与编译器有重大的相关,比如在win32环境下编译,在操作系统64位环境下,仍然是4字节。)

 

问:字符指针、浮点数指针、函数指针这三种类型的变量哪个占用的内存最大?为什么?

答:三者占用的内存一样大,因为所有指针变量所占的内存单元数量都是相同的。在32位平台下,三个不同类型的指针占用的内存都是4个字节。

 

问:用变量a给出下面的声明和定义

  1. a.一个整型数。答:int a;
  2. b.一个指向整型数的指针。答:int *a;
  3. c.一个指向指针的指针,它指向的指针是指向一个整型数。答:int **a;
  4. d.一个有10个整型数的数组。答:int a[10];
  5. e.一个有10个指针的数组,该指针是指向一个整型数的。答:int *a[10];
  6. f.一个指向有10个整型数数组的指针。答:int (*a)[10];
  7. g.一个指向函数的指针,该函数有一个整型参数并返回一个整型数。答:int (*a)(int);
  8. h.一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数。答: int (*a[10])(int);

分析:

分析同下一道面试题。

 

问:各种复杂指针的声明,下面的数据声明都代表什么意思?

a. float(**fa)[10]

答:fa是一个二级指针,它指向的是一个一维数组的指针,数组的元素都是float类型。

b. double *(*sp)[10]

答:sp是一个指针,它指向一个一维数组,该数组元素都是double*

c. double  (*arr[10])()

答:arr是一个数组,arr10个元素,元素都是函数的指针,指向的函数类型是没有参数且返回double的函数

d. int *((*a)[10])

答:与“int *(a)[10]”一样的,a是一维数组的指针。

e. long (*fun)(int)

答:函数指针。

f. int (*(*f)(int,int))(int)

答:f是一个函数的指针,指向的函数的类型是有两个int 参并且返回一个函数指针的函数,返回的函数指针指向一个int参数且返回int类型的函数。

分析:

笔者对于指针声明总结了一下技巧,有重要的两点:

第一点,用优先级来分析指针声明语句,优先级从高到低为:()>[]>*。第二点,从里到外分析,比如(*a)a先与*结合,再到()

接下来让笔者为大家分析一下怎么看懂多种指针的声明,比如最难的最后面那个指针声明:int (*(*f)(int,int))(int)

1. 找到变量f,由于*在()里面,f先与*号结合,然后是(),说明f是一个指针变量。即:(*f)

2. f再与右边的(int,int)结合,并且(int,int)中有两个参数,说明f指向一个有两个int参数的函数,即f是一个函数指针,(*f)(int ,int)

3. 如果觉得太长,难理解,笔者在这里把(*f)(int ,int)当作一个变量名NN先与*号结合,然后是外面的(),即(*N)是一个指针(确切地说函数指针N返回类型不是指针类型,通过(*N)之后,这个函数指针的返回类型是指针。 )。

4. (*N)与右边(int)结合,并且(int)有一个参数,说明(*N)指向有一个int参数的函数,即函数指针的返回类型指针指向一个有一个int参数的函数。

5. 由于最左边是int,则函数指针返回类型指针指向的是返回值为int型且有一个int参数的函数。

其实笔者认为在f点当中也可以这么说:含有返回两个int参数的函数指针返回类型指针指向一个int参数且返回int类型的函数。

只是这样说,易于理解。但是标准的说法是,把整个声明语句都拆分来讲的,比如函数指针也要拆开来说明,新手因为复杂理解比较困难而已。但是没关系,这并不重要,在程序设计时一般是不会使用这么复杂的定义的,因为这使程序的可读性变差,在这里只是为了学会分析即可。

 

问:指针指向的变量,那么请说说指针与数值运算的问题。

答:指针可能会指向意想不到的内存地址。

using namespace std; 

int main()

{

 int a = 100;

 int b = 200;

 int *p = &a;

 p = p - 1;//由于整型变量占4个字节,指针应该移动1*4 

 cout << &a << endl;

 cout << &b << endl;

 cout << p << endl;//此时指针指向了变量b,也有可能不是 

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

 printf("%d\n", &b);

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

 //注意,指针与数值进行运算适合运用在在数组,因为数组的元素是连续排列的。

   //而在这里,由于编译器处理内存不一样,可能要指向的地址就不会准确(在内存管理机制中,变量之间可能会有空隙空间,所以会留出空隙地址)。 

 return 0;

}

 

问:指针与变量的类型不一致时,请问怎么解决这个问题?

答:由于指针与变量的类型不一致,指针会指向不正确的内存地址,需要强制转换成和指向的对象的数据类型保持一致。

分析:

#include<iostream>

using namespace std;

int main()

{

//p的类型和a不一样时

 char a[15]= "mygirlislovely";

 int *p = (int *)&a;

 p++;//由于指针p是整型类型的,它移动4个字节,指向下一个地址会,指向了字符数组中的“r”元素

 cout << (char)*p << endl;//需要强制转换char类型可以输出字符 

 return 0;

}

 

问:分析一下,下面中定义的指针,说出指针类型、指针所指向的类型、指针的值和指针本身所占据的内存区?

答:

int a= 4;

int *p= &a;

指针类型:int* 指针所指向的类型:int 指针的值:4

char *p;

指针类型:char*  指针所指向的类型:char  

int **p;

指针类型:int**   指针所指向的类型:int

int (*p)[3];

指针类型:int (*)[3] 指针所指向的类型:int ()[3]

指针本身所占据的内存区:以上指针本身所占的内存大小,在32位程序中,为4个字节。

分析:

指针类型:把指针声明语句的指针名字去掉,就是指针类型,例如int *p;指针类型为int*

指针所指向的类型:把指针声明语句的指针名字和名字左边的*号都去掉。例如:int *p;指针所指向的类型是int

指针的值:指针所指向的变量的值。

 

问:什么是野指针?

答:“野指针”是在定义指针后没有对其进行初始化,或者指针指向的内存被释放,而指针没有被设置为NULL。野指针随机地指向一个地址,使用这个指针进行操作时,就会更改该内存的数据,造成程序数据的破坏,严重威胁着程序的安全。

 

问:什么是悬浮指针?

答:当所指向的对象被释放或者收回,但是对该指针没有做任何的修改,以至于该指针仍旧指向已经回收的内存地址,是悬浮指针,也称作是迷途指针,是造成野指针的一部分,属于野指针。

分析:

假如在进程A中,释放了指针所指向的内存M,然后操作系统把原来一部分已经释放掉的内存M重新分配给进程B。如果通过进程A中的迷途指针修改内存M的数据,由于内存M后来已经分配给进程B的,这将会产生无法预料的后果。这种错误是不易被发现的。

 

问:下面代码会编译通过吗?

 char *tabHeader = "Sound";

 *tabHeader = 'L';

 printf("%s\n", tabHeader);

答:

//字符串字面值作为常量,在vs编译器的环境下不能修改,出错。

//gcc环境下可以修改,即改变的是新的一块内存地址(拷贝一份),原来的常量的值并没有被改变。但是char前面加上const,编译也不会通过,

 char *tabHeader = "Sound";

 *tabHeader = 'L';//tabHeader指向的是常量的地址,不能修改

 printf("%s\n", tabHeader);

 

问:请根据以下代码讲述一下“*p,*p(++),(*p)++,*++p”之间的区别以及含义?

void main()

int b[5] = {9, 2, 3, 4, 5 };

 int *p = b; 

 for (int j = 0; j <4; j++)

 {

  cout <<b[j+1]<< "指针指向的内容:" << *++p << endl;//p++,后*p

 }

 p = b;//指针指向恢复原来数组的首地址,因为上一次循环会改变p的地址值 

 cout <<endl;

 for (int i=0;i<5;i++)

 {

  //*p(++)语法错误 

  cout << b[i]<<"指针指向的内容:" << *p++ << endl;//*p,后p++

 }

 p = b;

 cout << endl;

 //分开执行是因为下面循环的*p的内容改变

 for (int m = 0; m < 5; m++)

 {

  cout << "值:" << (*p)++ << endl;//指针所指向的内容加

 }

}

 

问:观察下面代码,说说“pp++”编译之后,为何没有输出结果?

char a[6] = "hello";

char *p = a;

char **pp = &p;

printf("%c\n", **pp);

pp++;

printf("%c\n", **pp);

答:因为pp++之后,pp指向的变量不再是p,指向的是p变量前面的一块未知内存,非法。

猜你喜欢

转载自blog.csdn.net/liubing8609/article/details/86144478
今日推荐