C语言笔试一些简单容易错的题目

1.字符串反转,比如输入abcd123,然后编程321dcba

#include<stdio.h>
#include<string.h>

int main(void)
{
    char buf[10];
      char t;
      int len;
      puts("enter a string:");
      fgets(buf,sizeof(buf),stdin);
      puts("before:");
      puts(buf); 
      len=strlen(buf);
      for(int i=0;i<len/2;i++)
      {
        t=buf[i];
        buf[i]=buf[len-1-i];
        buf[len-1-i]=t;

      }
      puts("after:");
      puts(buf);

      return 0;
}
 

注意:fgets(scanf函数不能接受空格,而fgets函数可以)函数第一个参数是缓冲区buf,第二个是buf-1的大小,因为我们还有一个“\0”要占最后一个字节。字符串反转大概思路就是:第一个字符串和最后一个字符串对调,第二个字符串和最后一个字符串对调,这样依次完成,因为我们最后一个字符是结束符,所以要在for循环里面-1;

2.define宏定义

#include<stdio.h>
#include<string.h>

#define M 5
#define N M+M


int main(void)
{
    int k;
    k=N*N*5; //M+M*M+M*5   5+25+25
    printf("%12d\n",k);

    return 0;
}

当时做错的原因:define N  M+M  ,M=5;我把M+M算出来,不能这样,是N替换M+M,只要遇到N就替换为M+M,而不是把M+M算出来为10;

3.++ i 是先加后赋值;i ++ 是先赋值后加;++i和i++都是分两步完成的

#include<stdio.h>
#include<string.h>

int main(void)
{
    
   int m=5;
   if(m++>5)
   printf("%d\n",m);
   else
   printf("%d\n",--m);

    return 0;
}
 

m++>5,是先赋值,也就是先在if里面判断,然后再++;判断m没有大于5,执行else,然后--m;--是先减,然后再赋值,m在上面++过了,变成了6,在下面是--m,然后再赋值,所以结果是5。如果是是m--,那么结果就是6,先赋值,再减。

4.指针和i++

#include<stdio.h>
#include<string.h>

int a[]={0,2,4,6,8};

int main(void)
{ 
    int i;
    int *p=a;
    for(i=0;i<4;i++)a[i]=*p++;
    printf("%d\n",a[2]);
    
    return 0;
}
 

解释:*p++就相当于*(p++),p先与++结合,然后p++整体再与*结合。*p++解析:++先跟p结合,但是因为++后置的时候,本身含义就是先运算后加1(运算指的是整体与前面的*进行运算;加1指的是p+1),所以实际上*p++符号整体对外表现的值是*p的值,运算完成后p再加1;等同于:*p,p+=1;

(*p)++这个的意思是:先去处p里面的值赋值给人家,再把p里面的值+1;

5.

void foo(void) {
unsigned int a = 6; 
int b = -20;
 (a+b > 6) puts("> 6") : puts("<= 6")
}输出的是什么

unsigned 是无符号数。 b 是有符号数负数,无符号数和有符号数混合运算 要注意 你到底 想计算什么。默认,把有符号数内容 当成无符号数解释 计算。-20 就当 0xffffffec (4294967276).显然(a+b > 6) 成立。输出 > 6。

6.三目运算符也叫条件运算符

如果希望获得两个数中最大的一个,可以使用 if 语句,例如:

if(a>b){
    max = a;
}else{
    max = b;
}

不过,C语言提供了一种更加简单的方法,叫做条件运算符,语法格式为:

表达式1 ? 表达式2 : 表达式3

运算规则是这样的:如果表达式1的值为真,则以表达式2 的值作为整个条件表达式的值,否则以表达式3的值作为整个条件表达式的值。

上面的 if else 语句等价于:

max = (a>b) ? a : b;  //a>b,就输出a,否则输出b

我们来看一道面试题

问题:写一个”标准"宏MIN ,这个宏输入两个参数并返回较小的一个。

#define Min(a,b) ( ((a)>=(b)) ? (b) : (a) )  

7.关键字const是什么含意?

    const int a;
  int const a;
  const int *a;
  int * const a;
  int const * a const;

首先理解一下 const int *a 和int *const a,去掉int,变成 const *a, 这样理解const 修饰 *a,*a就是a所指的那个数字,就是那个数字不能被改变。这样就比较好理解int *const a,去掉int,const修饰a,a是指针(里面存的地址)也就是里面的地址不能不改变。前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。

8.指针与指针相减

我们直接来看这道题,老爱忘记。

#include<stdio.h>


int main(void)
{

    int *p1,*p2,a[10];
    p1=a;
    p2=&a[5];
   printf("%d\n",p2-p1);

   return 0;
}

解析:任何编译器上都是5。指针减法含义:两个地址之间有多少个计量单位,那么p2-p1的意思就是p2和p1之间有多少个int,那么不管in是16位,还是32位,甚至是64位,和int的长度没有关系。

9.中断问题

中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断,从而产生了一个新的关键字 __interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码。

__interrupt double compute_area (double radius)

{

double area = PI * radius * radius;

printf("\nArea = %f", area);

return area;

}

答: aISR不能返回一个值。

        bISR不能传递参数。

        c、在许多处理器编译器中,浮点一般都是不可冲入的。有些处理器编译器需要让额外的寄存器入栈,有些处理器编译器就不允许在ISR中做浮点运算。此外ISR应该是短而有效率的。在ISR中做浮点运算是不明智的。

        dprintf经常是有冲入性和性能上的问题,所以一般不使用printf函数

10.++前置++后置

#include<stdio.h>


int main(int argc, char *argv)
{
   int a = 1, b = 1;
   int aplus, bplus;
   aplus = a++; //先把a的值赋值给aplus,a再加1
   bplus = ++b; //b的值先加1, 再把值赋值给bplus
   printf("a aplus b bplus\n");
   printf("%d  %d    %d   %d\n", a, aplus, b , bplus);
   return 0;
}

程序运行如下:

a aplus b bplus
2   1     2    2

#include<stdio.h>
#define TEN 10

int main(int argc, char *argv)
{
   int n = 0;
   while (n++ < TEN)
      printf("%5d",n);
   printf("\n");
   
   return 0;
}

程序运行如下:

    1    2    3    4    5    6    7    8    9   10

分析 :这里是先比较大小再加1, n一开始为0,0小于10,n+1,输入1,一直加到9小于10,9+1,所以输出10

#include<stdio.h>
#define TEN 10

int main(int argc, char *argv)
{
   int n = 0;
   while (++n < TEN)
      printf("%5d",n);
   printf("\n");
   
   return 0;
}

程序运行如下:

    1    2    3    4    5    6    7    8    9

分析:这里是先比较大小,再+1,相当于n = n+1;

11.指针偏移量的问题

#include <stdio.h>

int  main()
{
    int a[] = {1, 2, 3, 4, 5};
    int *p = (int *)(&a + 1);
    printf("%d %d \n", *(p - 1), *p - 1); 
    printf("%ld %ld\n", sizeof(a), sizeof(&a));
   
    return 0;
}
 

12.指针指向常量区

str1和st2的地址是不是一样的,结果是一样的为什么呢?

#include <stdio.h>

int main()
{
    char *str1 = "hello world";
    char *str2 = "hello world";

    printf("%p\n", str1);
    printf("%p\n", str2);

    char *str3 = str2;
    str3 = "NICE";

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

    return 0;
}

13.函数返回值一样吗?第二个会发生段错误

#include <stdio.h>

char* get_hello1();
char* get_hello2();

int  main()
{
    printf("%s\n",get_hello1());
    printf("%s\n",get_hello2());
    
    return 0;
}

char *get_hello1()
{
    char *str1 = "hello";

    return str1;
}

char *get_hello2()
{
    char str2[] = "hello";

    return str2;
}

14.指针的偏移量

#include<stdio.h>


int main(int argc, char *argv[])
{
    int a[] = {384, 385, 386};
    char *p = (char *)a;

    printf("%d\n", *p);
    printf("%d\n", *(p + 1));
    printf("%d\n", *(p + 4));
    

    return 0;
}

简单分析一下:大概意思就是4个字节,转换成1个字节。384的二进制是1 1000 0000,只取低8位就是1000 0000,也就是-128,*(p+1)就是指针移动1个字节,也就是到1的地方,结果就是1。

运行结果如下:

-128
1
-127

15.一道很不错的题目,对于复习大小端,和内存分布,指针偏移很有帮助。

#include<stdio.h>
/* 小端模式 */

int main(int argc, char *argv)
{
    int a[4] = {1, 2 ,3, 4};
    int *p1 = (int *)(&a + 1);
    int *p2 = (int *)((char *)&a + 1);
    printf("0x%x, 0x%x\n", *(p1-1), *p2);

    return 0;
}

先复习一下知识点,有点忘记了

a+1:就是数组首地址加上一个元素所占的地址大小,这里int是4个字节,所以加上1x4.
&a+1:代表的是加上整个数组的大小,这里数组尺寸是4,所以+1代表的是地址加上4x4.为了更好理解,画内存图来分析理解

 在这里插入图片描述

结果是0x4和0x2000000,0x2000000有点想不明白,看了“int *类型指针变量强制转换为char *的结果”,的博客就明白了,博客地址:

(85条消息) int *类型指针变量强制转换为char *的结果_肘子君的博客-CSDN博客_int指针强制转换成char指针

假如有这么一个程序

	int a = 0x44332211;
	int *p1 = &a;
	char *p2 = (char *)&a;
	printf("*p1 = 0x%x\n",*p1);
	printf("*p2 = 0x%x\n",*p2);

结果如下图

在这里插入图片描述

分析:
a,p1,p2在内存中的实际效果如下(地址是假设的)

p1和p2所指向的都是变量a的地址。

不同的地方在解指针的时候。
p1在解指针时,将0x00当作是四个字节的内存空间大小的首地址。然后以int的解析类型进行解析。也就是说在解指针时会将从0x00为起始的后面总共四个地址所包含的内存空间的数都以int的解析类型解析出来。
p2在解指针时,将0x00当作是一个字节的内存空间大小的首地址。然后以char的解析类型进行解析。也就是说在解指针时会将从0x00为起始的后面总共一个地址所包含的内存空间的数都以char的解析类型解析出来。

0x44332211在变量a指向的内存空间中的分布。
在这里插入图片描述

 *p1取a中的值时:四个字节作为整体一起解指针

在这里插入图片描述

 *p2取a中的值时:对一个字节的空间大小进行解指针

在这里插入图片描述

 由于p1和p2的指针类型不同。所以在对p1和p2做+1操作时,效果也会不同。
p1+1后,p1+1指向的内存地址为0x00+4=0x04;
p2+1后,p2+1指向的内存地址为0x00+1=0x01;

16.字符串大写字母转小写字母,代码比较简单,忘记的话就需要看一下ascii码

#include<stdio.h>
#include<string.h>

int main()
{
	int i, len;
	char s[101] = {0};
	gets(s);
	len = strlen(s);
	//大写变小写
	for(i = 0; i < len; i++) {
		if(s[i] >= 'A' && s[i] <= 'Z') {
			s[i] = s[i] + 32; /* 不明白就看一下ASCII码 */
		}
	}
	puts(s);
	return 0;
}

17.联合体(也叫共用体)就是所有成员共用一块空间,空间大小取决于最大成员的大小。这里是抖音看到的一道笔试题,是联合体里面的成员变量相加。还做错了。算成了3,想着被第三个成员占用了,就想当然的认为是0;

#include <stdio.h>

typedef union {
    int a;
    int b;
    struct {
        int c;
    } d;
} new_type;

int main(void)
{
    new_type dat;
    dat.a = 1;
    dat.b = 2;
    dat.d.c = 3;
    printf("%d\n", dat.a + dat.b + dat.d.c);
    
    return 0;
}

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值