1、指针,字符数组
若定义
- char s[2][3]={“ab”, “cd”}, *p=(char *)s;
那么下列表达式语法正确,并且其值与 s[1][1]相等的表达式(并非一定与其等价)是
A. *(s+3)
B. p[1][1]
C. *(p+3)
D. *++P+2
答案:D
分析:这道题很好。首先字符型指针p存放的是字符串s的首地址,而字符串s表示的是这样一个字符串:
ab_
cd_
为了便于理解,“_”表示空格,即没有元素。题中s[1][1]是d,所以要在ABCD中寻找表示s[1][1]的数。
先来看*(s+3),s表示数组首地址,不是指针型变量,所以s+3没有意义。p[1][1]同样由于p是指针,不存在这种表达。*(p+3)是是p表示的指针后移3位,因此*(p+3)是c。D选项有点复杂,*++p+2 这个比较有趣,p是一个char*指针,指向字符数组首地址,&s[0][0]。首先++的优先级更高执行++p得到的是&s[0][1],然后*运算符解引用,得到s[0][1],s[0][1]的字符是'b',然后'b'+2,得到的就是'd'的ASCII码.
下面是一道类似的题目:
若定义 char a[3][3]={“ad”, “ce” , “fb”}, *s = (char *)a; 那么下列表达式语法正确,并且其值与 a[2][1]相等的表达式是_______。
A.*(a+3) B.*(*a+5) C.s[2][1] D.*++s-2
答案是D,分析可参照:https://blog.csdn.net/zhanshen112/article/details/80786576
18、指针的指针
有函数原型为
- void f(int, int *);
,主函数中有变量定义:
- int a=2, *p=&a;
则下列函数调用正确的是
- A. f(a,&p);
- B. f(*p, p);
- C. f(p,a);
- D. f(*p, a);
- 答案:B
- 分析:注意p为指向整型的指针,*p则指向内存地址处所存放的数据。*p实际上就是a.
22、宏定义
下列程序段的输出是_______。
#define DF(a,b) (a+2*b)
int s=5;
int k= DF(s+1,s-3);
printf("%d",k);
答案:13
分析:k=DF(5+1,5-3)=5+1+2*5-3=13。注意宏定义中参数的传递,k=DF(5+1,5-3)=(5+1)+2*(5-3)=10是错误的。
23、全局变量、局部变量、静态变量的生存期
以下代码的输出是:
int x, y, z, w;
void p(int *y, int x)
{
static int w;
*y++; x++; w = x+*--y;
printf("%d#%d#%d#%d#",x,*y,z,w);
}
int main(void)
{
int x, y, z, w;
x=y=z=w=1;
do{
static int x;
p(&x, y);
printf("%d#%d#%d#%d#",x,y,z,w);
} while(0);
return 0;
}
答案:2#0#0#2#0#1#1#1#
解析:主要考察局部变量和全局变量的生存期,以及静态本地变量。注释后的代码如下:
int x, y, z, w; //这里是全局变量,定义在任何函数的外面,若不初始化赋值,则均为0;。注意,主函数里面的变量仍为局部变量
void p(int *y, int x) //p函数接收整型指针变量和整型变量的输入,返回值为空
{
static int w; //定义静态变量w,若不初始化赋值,则w==0;
*y++; x++; w = x+*--y;
printf("%d#%d#%d#%d#",x,*y,z,w);
}
int main(void)
{
int x, y, z, w;
x=y=z=w=1;
do{
static int x;
p(&x, y);
printf("%d#%d#%d#%d#",x,y,z,w);
} while(0);
return 0;
}
从主函数进行分析,主函数内部定义了四个int型变量,若不进行初始化,则全为0。主函数内部定义完之后就进行了初始化,均初始化为1,所以在do-while内部,可以看到由于只定义了静态局部变量x,而且没有初始化赋值,则静态局部变量x为0,y,z,w均仍为1。所以
printf("%d#%d#%d#%d#",x,y,z,w);
的输出是0#1#1#1#。
再来分析p这个函数:
void p(int *y, int x) //p函数接收整型指针变量和整型变量的输入,返回值为空
{
static int w; //定义静态变量w,若不初始化赋值,则w==0;
*y++; x++; w = x+*--y;
printf("%d#%d#%d#%d#",x,*y,z,w);
}
首先p函数无返回值,接受两个输入:int型指针变量、int型变量。p函数内部同样定义了一个静态局部变量w,但是w后面有赋值的语句
w = x+*--y;
*y++和x++是两个关键,首先*和++,--运算符处于同一优先级,结合方向是自右向左。因此*y++可以看做是*(y++),但是由于y++是先执行y,跳出去与*结合,再让y++。所以*y++实际上等效于先执行*y操作,再执行y++。由于y是指针,因此y++是指针所指内存地址的向后移动,移动的大小是一个sizeof(int)。x++同理先执行x(由于没有任何操作,x不变),再让x+1,这里实际上由于x没有任何操作,x++相当于只执行了x+1,由于局部变量传入p函数的x为1,这里x就等于2了。
由于y是指针变量,因此*y表示取出指针所指内存地址的值。由于传进去的
static int x;
p(&x, y);
x是静态变量,则x=0,因此*y=0。这里要注意p的原型是void p(int *y, int x) ,而使用p函数时,传进去的是
p(&x, y);
顺序不要搞反了。w=x+*--y,这里等价于w=x+*(--y),由于在上面的*y++,y已经执行了y+1,这里(--y)先执行--,再执行y,即先执行y-1,再把y-1的结果传出去。注意这里的-1指的是减去一个int型变量的内存大小。因此y还是原来的内存位置。所以*y还是取出原来指针指向内存地址的值,即还是原来的静态局部变量x,值为0。因此w=x+*--y中x=2,(*--y)等于0,所以w=2。由于p函数里的z只能那个接受全局变量,因此z=0,所以p函数执行之后打印:2#0#0#2。
24、sizeof和数组
假设sizeof(int)的值为4,对数组定义:
- int a[3][6];
则sizeof(a[0])的值为____.
答案:24
解析:a[0]里面有a[0][0],a[0][1]……a[0][5],共6个元素,均为int,因此4*6=24.
25、条件表达式以及&&,||,!
写出表示“当 x 的取值在 [-10, 0] 的范围内,结果为真,否则为假”的C语言表达式,注意不要任何空格
答案:x>=-10&&x<=0 或 x<=0&&x>=-10 或 !(x<-10||x>0)
解析:我的答案是(x>=-10)&&(x<=0)?1:0,不如参考答案。
26、&&,||,!
若 int a = 6, b = 0, c = 3,则表达式 a && b || b - c的结果是(以1表示真,0表示假)
答案:1
27、指针、字符串和数组
以下代码段的输出是:
char a[20]="cehiknqtw";
char *s="fbla",*p;
int i, j;
for(p=s; *p; p++) {
j=0;
while (*p>=a[j] && a[j]!='\0') j++;
for(i=strlen(a); i>=j; i--) a[i+1] = a[i];
a[j]=*p;
}
printf("%s", a);
答案:abcefhiklnqtw
分析:这段代码具有一定的难度。首先a[20]是一个字符数组,s,p是字符型指针。s刚开始是“fbla”的首地址,因此*s实际上是指向f的内存地址的值,即s存放的是f的地址。
核心部分是for循环中的语句,
p=s; *p; p++
是将s的指针赋给p,然后逐渐后移,也就是把s字符串中的字符指针逐渐都进行赋值。即,p会逐渐等于f,b,l,a的地址。
while (*p>=a[j] && a[j]!='\0') j++;
这个语句的意义是在a[]中寻求大于*p所指的字符,当*p==f时,a[]中的h是大于f的,此时j==2,程序跳出while循环,进入内层for循环,内层for循环的意义是把j==2之后的字符都进行后移一位,为*p==f腾出位置,然后将a[2]=*p,也就是把s中的第一个字符填入a[]中。下面依次填入s中其他的字符,因此这个程序完成的任务实际上是把s中的字符按照字符间ASCII数值的大小关系填入a[]中。
28、宏定义
根据下面的定义,F0(3+4)的输出结果是_______(注意没有空格)
#define F1(var) printf("var=%d", var)
#define F0(var) F1(var * var)
答案:var=19
29、指针的指针
程序T1的代码如下,则运行T1 abc bcd cde aed的输出结果是_______.
int main(int argc, char** argv)
{
while(**argv++!='a');
printf("%s", *argv);
return 0;
}
答案:bcd
分析:
解释一:程序进来后,argv的内容是 T1 abc bcd cde aed
while(); 注意while循环后面有分号,说明printf是在循环之后才执行的。
**argv++相当于
-
return argv;
-
return **argv
-
argv++ (每执行一次,将会指向下一个字符串)
当执行第二次循环时, abc,此时**argv为'a',循环结束。在此之后,argv++指向了下一个字符串,也就是bcd。
解释二:
1、int main(int argc, char** argv)表示当执行程序时可以带上参数,所以题目中执行时就写为T1 abc bcd cde aed,可理解为要执行一个名为T1的程序,并需要对abc bcd cde aed这几个字符串进行处理
2、argc表示参数的个数,此处argc=5,即T1 abc bcd cde aed这5个
3、argv表示的是命令行参数,char** argv可以看成char* argv[],即一个字符串数组,每个元素对应一个字符串,值为字符串的首地址。因此**argv就是字符串的首字母
4、**argv++!='a'就表示当字符串的首字母不等于a时,则跳过该字符串,继续判定下个字符串。一旦发现某个字符串首字母为a,则在argv++作用下输出下一个字符串。比如检测第一个字符串abc时发现首字母为a,则跳出while循环,并在argv++作用下输出bcd
5、**argv++优先级可以看成**(argv++)
以下是一些测试(我的文件名是Cpp1.exe,不影响),就可以看出规律了
补充:**的用法
*表示指针,**表示指针的指针。
例如:int *a;这个语句声明了一个变量a,a的数据类型是int *,也就是整型变量的指针类型(如果不懂什么是指针,那这个问题就没有意义了)。也就是说 a的值是一个内存地址,在这个地址所在的内存空间中存放的是一个整型变量。再看:int **b;这个语句也声明了一个变量b,b的数据类型是int **,也就是整型变量的指针的指针类型(二级指针)。也就是说 b的值是一个内存地址,该地址所在的内存空间中存放的是一个整型变量的指针(一级指针,或许就是上面那个a的值)。
30、变量的指针
以下代码的输出是 :
void swap( int *pa, int *pb )
{
int pt;
pt = *pa, *pa = *pb, *pb = *pa;
}
int main(void)
{
int x=1, y=2;
swap(&x, &y);
printf("%d%d", x, y);
}
答案:22
分析:看清楚swap函数,不是交换两个变量的地址,而是把pb的地址又赋给pa,即pa,pb均指向pb的地址。其实没必要这么麻烦,直接把pb的地址给pa就行。