【C语言】C语言的基础知识以及基础试题(对基本知识点的掌握)

"这篇博客探讨了C语言中关于指针、数组和内存管理的知识点,包括sizeof运算符的应用、函数参数传递、字符串常量的处理、内存分配和释放的陷阱、以及C++调用C函数时的extern"C"问题。文章通过实例解释了数组名、指针变量和内存地址的关系,并介绍了如何正确地进行内存操作和函数参数传递,以避免程序异常和内存泄漏。此外,还讨论了二维数组的表示方式和指针运算的细节。"
摘要由CSDN通过智能技术生成
  1. 以下为Linux下的32 位C 程序,请计算sizeof 的
    值。
char  str[] = “Hello” ;                               
char   *p = str ;                                      
int    n = 10;  
void Func ( char str[100])
void * p = malloc( 100 );

请计算
(1)sizeof (str ) =
(2)sizeof ( p ) =
(3)sizeof ( n ) =
(4)sizeof( str ) =
(5)sizeof( p) =
【标准答案】
(1)6、(2)4 、(3 )4、(4)4、(5)4

sizeof(数组名) 的值是这个数组所占据的内存的大小,单位是字节(bytes)

void Func ( char str[100])
在C/C++里数组作为参数时传递的实际上是指向数组第一个元素的指针,因此sizeof(str)返回的是指针的大小,即4。
如果没有函数直接char str[100]; sizeof(str)是100
注意以下三种形式的参数是完全等价的:
void Func(char str[100]);
void Func(char str[]);
void Func(char *str);

  1. 用变量a 给出下面的定义
    e) 一个有10个指针的数组,该指针是指向一个整型数
    的;
    f) 一个指向有10个整型数数组的指针;
    g) 一个指向函数的指针,该函数有一个整型参数并返
    回一个整型数;
    h) 一个有10个指针的数组,该指针指向一个函数,该
    函数有一个整型参数并返回一个整型数;
    【标准答案】e)int * a[10]、 f)int (*a)[10]、 g)int (*a)(int)、 h) int (*a[10])(int)
  2. 设有以下说明和定义:
typedef union {long i; int k[5]; char c;} DATE;    // 20
struct data { int cat; DATE cow; double dog;} too;  // 32
DATE max;

则语句printf(“%d”,sizeof(struct date)+sizeof(max)); 的
执行结果是:_____
【标准答案】DATE是一个union, 变量公用空间. 里面最大的变量类型是int[5], 占用20个字节. 所以它的大小是20.
data 是一个struct, 每个变量分开占用空间. 依次为int4 + DATE20 + double8 =32. 所以结果是32+ 20 = 52.
4. 请问以下代码有什么问题:

int main()
{
    char a;
    char *str=&a;  //错误,没有为str分配内存空间
    strcpy(str,"hello");  //错误,越界
    printf(str);
    return 0;
}

【标准答案】没有为str分配内存空间,将会发生异常
问题出在将一个字符串复制进一个字符变量指针所指地址。虽然可以正确输出结果,但因为越界进行内在读写而导致程序崩溃。
5.

char* s="AAA";  //错误
printf("%s",s);
s[0]='B';  //错误
printf("%s",s);

有什么错?
【标准答案】“AAA” 是字符串常量。s是指针,指向这个字符串常量,所以声明s的时候就有问题。cosnt char* s=“AAA”;
然后又因为是常量,所以对是s[0] 的赋值操作是不合法的。

或者改为:char str[]=“aaa”; char *s=str;

C语言规定,可以将字符串直接赋值给字符数组,例如: char str[30] = “aaaaaaa”;

char *s 只是一个保存字符串首地址的指针变量, char a[] 是许多连续的内存单元,单元中的元素为char 。

char *s; char a[] ;
可以 s = a;
不能 a = s;

  1. int (*s[10])(int) 表示的是什么啊
    【标准答案】int (*s[10])(int) 函数指针数组,该函数有一个整型参数并返
    回一个整型数;
void getmemory(char *p) 
{
    p=(char *) malloc(100);
    strcpy(p,“hello world”);
}
int main( )
{
    char *str=NULL;
    getmemory(str);  //错误
    printf(%s/n”,str);
    free(str);
    return 0;
} 

会出现什么问题?
【标准答案】程序崩溃
调用GetMemory( str )后, str并未产生变化,依然是NULL.只是改变的str的一个拷贝的内存的变化,getmemory中的malloc 不能返回

修改方法一:如果非得用指针参数申请内存,可以用指针的指针作为参数申请内存

void GetMemory(char **p, int num)
{
    *p = (char *)malloc(sizeof(char) * num);
}
int main()
{
    char *str = NULL;
    GetMemory(&str, 100);     //记得加地址符    
    strcpy(str, "hello");        
    free(str);
    return 0;
 }

修改方法二:传指针的引用

void GetMemory(char *&p, int num)
{
    *p = (char *)malloc(sizeof(char) * num);
}
int main()
{
    char *str = NULL;
    GetMemory(str, 100);     //记得加地址符    
    strcpy(str, "hello");        
    free(str);
    return 0;
 }
void main()
{
    char aa[10];
    printf(%d”,strlen(aa));
}           

会出现什么问题?打印结果是是多少?
【标准答案】sizeof()和初不初始化,没有关系,strlen()和初始化有关,打印结果值未知。

strlen在计算数组长度的时候是遇到‘\0’时计算就停止。
如果不在字符数组后面加上‘\0’,那么strlen就会一直计算下去,直到遇到内存中的’\0’,才停止计算。
可以看到在内存的某处存在‘\0’,由于strlen会遇到‘\0’时才停止计算长度,所以算出来的长度并不是字符数组的真实长度,而且这个值是随机的。

  1. 32位编译器
    • char :1个字节 char*(即指针变量): 4个字节
    • short int : 2个字节 int: 4个字节 unsigned int : 4个字节
    • float: 4个字节 double: 8个字节
    • long: 4个字节 unsigned long: 4个字节 long long: 8个字节
  2. 给定结构
struct A
{
     char t:4;    1字节占4bite
     char k:4;    1字节占4bite
     unsigned short i:8;     2字节占8bite
     unsigned long m;      4字节
};

问sizeof(A) = ?
【标准答案】8
11. 程序哪里有错误

swap( int* p1,int* p2 )
{
    int * p;
    *p = *p1;
    *p1 = *p2;
    *p2 = *p;
}

【标准答案】
p 为野指针(指向一个已删除的对象或未申请访问受限内存区域的指针),导致程序运行的崩溃
更改:

swap( int* p1,int* p2 )
{
	int p;
	p = *p1;
	*p1 = *p2;
	*p2 = p;
}
  1. (void * )ptr 和(* (void**))ptr 的结果是否相同?其中ptr为同一个指针。
    【标准答案】
(void *)ptr 和(*(void**))ptr 值是相同的
(void *)ptr 强制转化为void的一级指针。
(*(void**))ptr: void**是一个指向空类型指针的指针(-》一级指针-》地址),
然后*(一个指向空类型指针的指针)=指向空类型的一级指针(取地址)。
(void *)ptr 将变量ptr的值强制转换为 void(无类型)的指针
(*(void**))ptr 将变量ptr的值强制转换为 *(void**),即把一个指向(void类型指针)的指针,
求他的值,结果相当于括号里面的(void类型指针)
这两个的效果是完全一样的,用前面那种就可以了
  1. 用宏定义写出swap(x,y),即交换两数。
    【标准答案】
#define swap(x, y) (x)=(x)+(y);(y)=(x)(y);(x)=(x)(y);

宏定义多个语句之间必须有分号,最后也要加上分号
宏定义的单个语句后面没有分号 例如: #define SUM (a,b) ((a) + (b))

  1. 写一个“标准”宏,这个宏输入两个参数并返回较小的一个。
    【标准答案】
#define Min(X, Y) ((X)>(Y)?(Y):(X))    // 结尾 没有 ;
  1. 已知一个数组tabl e ,用一个宏定义,求出数据的元素个数。
    【标准答案】
#define NTBL(table) (sizeof(table)/sizeof(table[0]))
  1. A.c 和B.c两个c文件中使用了两个相同名字的static变量,编译的时候会不会有问题?这两个static变量会保存到哪里(栈还是堆或者其他的)?
    【标准答案】static的全局变量,表明这个变量仅在本模块中有意义,不会影响其他模块。
    他们都放在静态数据区,但是编译器对他们的命名是不同的。
    如果要使变量在其他模块也有意义的话,需要使用extern 关键字。
  2. 用两个栈实现一个队列的功能?要求给出算法和思路!
    【参考答案】设2个栈为A,B, 一开始均为空.
    入队:
    将新元素push入栈A;
    出队:
    (1)判断栈B 是否为空;
    (2)如果不为空,则将栈A中所有元素依次pop 出并
    push到栈B;
    (3)将栈B 的栈顶元素pop 出;
  3. 在C++ 程序中调用被C 编译器编译后的函数,为什么要加extern “C”?
    【标准答案】C++ 语言支持函数重载,C 语言不支持函数重载。
    函数被C++ 编译后在库中的名字与 C 语言的不同。假设某个函数的原型为:void foo(int x, int y); 该函数被C 编译器编译后在库中的名字为_foo ,而
    C++ 编译器则会产生像_foo_int_int之类的名字。 C++提供了C 连接交换指定符号extern“C”来解决名字匹配问题。

extern "C"的惯用法

(1)在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:

extern "C"
{
	#include "cExample.h"
}

而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern "C"时会出现编译语法错误。

/* c语言头文件:cExample.h */
#ifndef C_EXAMPLE_H
#define C_EXAMPLE_H
 
extern int add(int x,int y);

(2)在C语言中引用C++中的函数和变量时,C++的头文件需添加extern “C”,但是在C语言中不能直接引用声明了extern "C"的该头文件,只需要在C源文件中将C++中定义的extern "C"函数声明为extern类型。

//C++头文件 cppExample.h
#ifndef CPP_EXAMPLE_H
#define CPP_EXAMPLE_H
 
extern "C" int add( int x, int y );
 

 
//C++实现文件 cppExample.cpp
#include "cppExample.h"
int add( int x, int y ) {
    return x + y;
}
 
// C实现文件 cFile.c
/* 这样会编译出错:#include "cppExample.h" */
 
extern int add( int x, int y );
int main( int argc, char* argv[]) {
    add( 2, 3 );
    return 0;
}
#include <stdio.h>
int main()
{
    int a,b,c,d;
    a=10;
    b=a++;  //b=a=10,完了之后,a++等于11
    c=++a;  //a=11,先加1, c=12
    d=10*a++; //先10*a(12)=120,d=120,执行完,a再++
    printf("b,c ,d:%d,%d,%d",b,c,d );
    return 0;
}

【标准答案】10,12,120
20.

unsigned char *p1;
unsigned long *p2;
p1=(unsigned char *)0x801000;
p2=(unsigned long *)0x810000;

请问p1+5= ; //p1是char,加5就是15=5个字节
p2+5= ; //p2是long,加5就是4
5=20个字节
【标准答案】0x801005、0x810014
p1指向字符型,1个字节;p1+5后移5个字节,16进制表示为5;
p2指向长整型,4个字节,p2+5后移20字节,16进制表示为14。
{ char每次移动1个字节;short移动2个字节 ;int , long ,float移动4个字节 ;double移动8个字节}
21. int a[10],a代表什么,sizeof(a)=?
【标准答案】40
int a[10]:声明了一个有10个int类型元素的数组, 数组下标从0~9, 所以a是数组名, 代表数组的首地址, 也就是&a[0], sizeof(a)返回数组a在内存中所占的空间大小,以字节为单位, 也就是sizeof(a) = sizeof(int) * 10 = 4 * 10 = 40bytes
22. 一维数组

int main()
{
    int a[5]={1,2,3,4,5};
    printf(%d”, a);     //a是数组首地址  004EFD18
	printf(%d”, &a);   //&a是数组首元素的地址  004EFD18
	printf(%d”, a+1);     //a+1:就是数组首地址加上一个元素所占的地址大小,这里int是4个字节,所以加上1x4.  004EFD1C
	printf(%d”, &a+1);   //&a+1:代表的是加上整个数组的大小,数组大小是3,所以+1代表的是地址加上3x4    004EFD24
	printf(%d”, *a+1);  //*a+1代表的是数组第1个元素的值1,再加上1等于2
	printf(%d”, *(a+1));  //*(a+1)代表的是数组第二个元素的值  2
	printf(%d”, *(&a+1));  //*(&a+1):代表&a+1地址的值,未知
	int * ptr=(int*)(&a+1);
	printf(%d”, *(ptr-1));  // *(ptr-1) == *(*(&a + 1)-1) : 表示&a+1地址的前一个元素的地址   5

数组名是首元素的地址;下面两种条件除外:
sizeof(数组名),这里表示数组整个大小,不是数组首元素地址的大小;
&arr,表示的是整个数组的地址,不是数组首元素的地址。

  1. 二维数组 a[i][j]
[1] *a:以a[0][0]为首元素的一维数组.可带一个索引:(* a)[i] 即a[0][i]
[2] a:以a[0][0]为首元素的二维数组.可带两个索引:a[i][j]
[3] a[0]:同*a
[4] a[0][0]:首元素
[5] a[1]:以a[1][0]为首元素的一维数组. 代表第二行的首地址
[6] &a[1]:以a[1][0]为首元素的二维数组.可带两个索引:(&a[1])[i][j]即a[i+1][j]
[7] *(*a+1)---等价于a[0][1]     a[0][i]
[8] *(*(a+1))---等价于a[1][0]    a[i][0]
[9] *(&a[0][0]+1)--等价于a[0][1]
[10] *(a[1]+1) --等价于a[1][1] 
[11] *(a+1)是数组元素。但a是二维数组,那你就要再进一步理解了。
因为a是二维数组名,即是一维数组a[0] 、a[1] 、a[2] 的首地址。
a+1就是 a[1]的地址。
*是取指针的值。
那么*(a+1)就是取 a[1]的值。
它是什么呢?它又是一个数组: a[1][0], a[1][1] ,a[1][2]。
而且 a[1]是它们的首地址。
即 a[1]的值就是 a[1][0]的地址。
  1. 若有说明:int a[3][4];则对a数组元素的非法引用是( D )
    A:a[0][2*1]
    B:a[1][3]
    C:a[4-2][0]
    D:a[0][4]
    对于 a[3][4]说明有12元素,行从0到2,列从0到3。a[0][4]的列超界。

  2. 若二维数组a有m列,则计算任一元素a[i][j]在数组中是第( D )个数
    A:i * m + j B:j * m + i
    C:i * m + j – 1 D:i * m + j + 1
    在这里插入图片描述
    a[2][3]将获取数组中第 3 行第 4 个元素。第12个数,2*4+3+1=12

int modifyvalue()
{ 
    return(x+=10);
}
int changevalue(int x )
{
    return(x+=1);
}
void main()
{
    int x =10;    // 10
    x++;        // 11
    changevalue(x); //传递形参不改变实参,x还是11
    x++;        // 12
    modifyvalue();
    printf("First output:%dn",x);   // 12
    x++;         // 13
    changevalue(x);
    printf("Second output:%dn",x);   // 13
    modifyvalue();
    printf("Thirdoutput:%dn",x);     // 13
}

输出?
【标准答案】12、13、13
27. 下面的代码输出是什么,为什么?

void foo(void)
{
    unsigned int a = 6;
    int b = -20;
    (a+b> 6)? puts("> 6") : puts("<= 6");
}

【参考答案】这个问题测试你是否懂得C 语言中的整数自动转换原则,
我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答
案是输出是“>6” 。

原因是当表达式中存在有符号类型和无符号类型时所有的数都自动转换为无符号类型。

因此-20 变成了一个非常大的正整数,所以该表达式计算出的结果大于6 。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。
28. 评价下面的代码片断:
unsigned int zero = 0;
unsigned int compzero = 0xFFFF; //错误
【参考答案】对于一个int型不是16位的处理器为说,上面的代码是不正
确的。应编写如下:unsigned int compzero = ~0;
~是位运算符,是取反的意思,即二进制位0变1,1变0;
unsigned int compzero = 0xFFFF;表示1111 1111 1111 1111,对于int型不是16位的处理器来说,上面的代码是不正确的。
unsigned int compzero=~0;(以16位处理器来说明)表示将0的二进制位0000000000000000取反,变成1111111111111111,对不是16位处理器的也是正确的。
29. 下面的代码片段的输出是什么,为什么?

char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
    puts("Got a null pointer");
else
    puts("Got a valid pointer");

【参考答案】
malloc申请一段长度为0的空间,malloc依然会返回一段地址,还有一段地址空间,所以ptr不等于NULL。
malloc这个函数,会有一个阈值,申请小于这个阈值的空间,那么会返回这个阈值大小的空间。
如阈值为24,那么申请小于24的值就会返回24
这个阈值会随着编译器的不同而不同
如,在编译后,运行结果会返回12
如果申请一个负数,那么返回的是0 ,因为malloc规定不可以申请一个负数
30. 请编写一个C 函数,该函数将给定的一个字符串转换成整数。atoi
【参考答案】

int Invert(char* str) 
{ 
    int num =0; 
    while(*str!='\0') 
    { 
//将字符串每个位置的字符进行对应的ASCII码转换
        int digital=*str-48;   //字符 '0'-'9' 对应的十进制整数是48~57,将对应的整数减去48就得到了对应的整数
        num=num*10+digital; 
        str=str+1; 
    } 
    return num; 
} 
  1. 请编写一个C 函数,该函数将给定的一个整数转换成字符串。itoa
    【参考答案】
void IntToCharChange(int num,  char* pval) 
{ 
    char strval[100]; 
    int i , j; 
    int val0 = 0; 
    int val1 = 0; 
    val0 = num; 
    for(i=0; i<100; i++) 
    { 
        val1 = val0 % 10; //取余
        val0 = val0 / 10; // 取整
        strval[i] = val1 + 48;  // 数字—字符
        if(val0 < 10) 
        { 
            i++; 
            strval[i] = val0 + 48; 
            break; 
        } 
    } 
    for(j=0; j<=i; j++)  // 倒置
    	pval[j] = strval[i-j]; 
    pval[j] = '\0'; 
}
  1. 以下程序的功能是将字符串s中所有的字符c删除,那么空白处缺少的语句为:( A )。
#include 
int main(void)
{
    char s[80] ;
    int i, j ;
    gets(s) ;
    for ( i = j = 0 ; s [i] !=/0' ; i++ )
    if ( s [i] != ‘c' )
          ;
    s [ j ] =/0' ;
    puts ( s ) ;
    return 0 ;
}

A: s [ j++] = s [ i ] B:s [ ++j ] = s [ i ]
C:s [ j ] = s [ i ]; j++ D:s [ j ] = s [ i ]
33.

#include stdio.h
int main(void)
{
    int a = 2, i;
    for( i = 0; i < 3; i++ ) 
        printf("%4d",f(a));  //%4d  输出宽度为4,且右对齐
//%-4d  输出宽度为4,且左对齐
}
int f(int a)
{
    int b = 0;
    static int c = 3;
    b++; c++;
    return(a+b+c);
}

A:7 7 7 B:7 10 13
C:7 9 11 D:7 8 9
【参考答案】 D
每次调用函数f的参数都是a=2,三次参与a+b+c运算时都是a=2
b是局部变量, 每次初始化为0,之后自加为1。三次参与a+b+c运算时都是b=1
c是静态变量, 每次是上次退出值。 每次调用c自加。 所以三次参与a+b+c运算时, c依次为4 5 6
于是返回值依次为7 8 9
34. 以下程序的运行结果为( B )。

void sub(int x, int y, int *z)
{ *z = y - x ; }
int main(void)
{
int a,b,c ;
sub(10, 5, &a) ;  //传地址
sub(7, a, &b) ;
sub(a, b, &c) ;
printf( "%4d,%4d, %4d/n",a,b,c);
}

A:5, 2, 3 B:-5, -12, -7
C:-5, -12, -17 D:5, -2, -7
35. 有如下语句int a = 10, b = 20, *p1, *p2; p1 = &a; p2 = &b;变量与指针的关系如图1所示;若要实现图2所示的存储结构,可选用的赋值语句为( B )
在这里插入图片描述

A:*p1 = *p2 B:p1 = p2
C:p1 = *p2 D:*p1 = p2
*p1 = *p2; // 等效于 a = b;
p1 = *p2; // 类型失配,编译出错或警告
*p1 = p2; // 将b的地址转化为整数赋给a
p1=p2,p1=&b都可以啊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值