盘点那些C语言中你容易忽略的知识点

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

在阅读陈正冲写的《C语言深度剖析》的 过程中,发现自己掌握的C语言还不是很深入,很多细节之前学习的时候都没有注意到,现将其中的知识点进行总结:


目录

1、sizeof怎么使用?是关键字还是函数?   

2、C语言中有多少个关键字?你能想出多少个?

3、C语言的基本数据类型是哪几个?

4、字符串以'\0'结尾,那么'\0'对应的ASCII是什么?如何通过编程得到'\0'?

5、case关键字后面的值有什么要求?

6、const修饰指针时不同 位置的不同效果?

7、extern关键词什么作用?

8、struct和class的区别是什么?

9、union关键字的特点是什么?

10、大小端的特点是什么?

11、enum关键字有什么用?

12、typedef关键词的功能是什么?

13、C语言中 反斜杠(\)有哪些作用?

14、‘a’和“a”一样吗?

15、老生常谈的问题:指针和数组有什么关系?

16、数组指针和指针数组的区分

17、逗号表达式了解一下?

18、二维数组作为参数和二维指针作为参数的写法

19、堆、栈和静态区各存放什么数据?

20、指针没有指向合法内存什么意思?

21、内存泄漏了解一下?

附1:有趣的编程问题一则

附2:另一则有意思的编程题(不使用任何变量编写 strlen 函数)



1、sizeof怎么使用?是关键字还是函数?   

我们一般以sizeof(int)这样的方式来获取某种类型在内存中所占的空间大小,其形式很像函数吧?但是实际上sizeof是C语言的关键词。正因为sizeof是关键字,它还可以这样使用:

int i = 0;
sizeof i            //4
sizeof(int)         //4
sizeof(i)           //4
sizeof int          //Error,不能这样使用

即:sizeof在计算变量所占空间大小时括号可以省略。计算类型所占空间大小时括号不能省略。

2、C语言中有多少个关键字?你能想出多少个?

C语言中共有32个关键词。 

3、C语言的基本数据类型是哪几个?

C语言的基本数据类型可以分为:整型(短整型、整型、长整型)、浮点型(单精度型、双精度型)和字符类型。分别对应short, int, long, float, double, char。

在32位机器上所占的内存大小依次为:short(2 byte), int(4 byte), long(4 byte), float(4 byte), double(8 byte), char (1 byte)。

4、字符串以'\0'结尾,那么'\0'对应的ASCII是什么?如何通过编程得到'\0'?

'\0'对应的ASCII码为0(NULL)。

整理了几种方法得到'\0',方法一:

int i = 0;
char chr = (char)i;

方法二:

char chr = NULL;

理解了这个问题,来看一道题目:

int main() 
{ 
    char a[1000]; 
    int i; 
    for(i=0; i<1000; i++) 
    { 
        a[i] = -1-i; 
    } 
    printf("%d",strlen(a)); 
    return 0;
}

答案应该是多少呢?你能解决 这个问题吗?(255)

5、case关键字后面的值有什么要求?

case后面只能是整型或者字符型的常量或常量表达式。

6、const修饰指针时不同 位置的不同效果?

const int *p; // p 可变,p 指向的对象不可变 
int const *p; // p 可变,p 指向的对象不可变 
int *const p; // p不可变,p 指向的对象可变 
const int *const p; //指针 p 和 p 指向的对象都不可变

区分方法:看 const 离哪个近,离谁近就修饰谁。

7、extern关键词什么作用?

extern 可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,下面的代码用到的这些变量或函数是外来的,不是本文件定义的,提示编译器遇到此变量和函数时在其他模块中寻找其定义。

8、struct和class的区别是什么?

struct的 成员默认是public的,而class的成员默认是private的。

9、union关键字的特点是什么?

1.所有的数据成员共用一个空间,同一时间只能存储一个数据成员的值。所有数据成员有相同的起始地址。

2.union的成员是public的,只配置一个足够大的空间来容纳最大长度的成员。

3.可以利用union的特性来确定系统的大小端。

10、大小端的特点是什么?

大端模式表示字数据的高字节存储在低地址,而字数据的低字节存储在高地址;小短模式表示字数据的高字节存储在高地址,而字数据的低字节存储在低地址。可以这样记忆:大端模式高对应低,小端模式高对应高。一个存在地址0x100处的int型的变量x的16进制表示为0x01234567,则其大小端的表示如下图所示:

11、enum关键字有什么用?

一般地,枚举类型定义 方式如下:

enum enum_type_name
{
    ENUM_CONST_1,
    ENUM_CONST_2,
    ...
} enum_variable_name;

实际上enum_type_name 类型是对一个变量取值范围的限定,而花括号内是它的取值范围,即 enum_type_name 类型 的变量 enum_variable_name 只能取值为花括号内的任何一个值,如果赋给该类型变量的值 不在列表中,则会报错或者警告。ENUM_CONST_1、ENUM_CONST_2、...、 ENUM_CONST_n,这些成员都是常量,也就是我们平时所说的枚举常量(常量一般用大写)。 

enum 变量类型还可以给其中的常量符号赋值,如果不赋值则会从被赋初值的那个常量开始 依次加 1,如果都没有赋值,它们的值从 0 开始依次递增 1。

如分别用一个常数表示不同颜 色: 

enum Color
{
    GREEN = 1, 
    RED, 
    BLUE, 
    GREEN_RED = 10, 
    GREEN_BLUE
}ColorVal;

其中各常量名代表的数值分别为:GREEN = 1、RED = 2 、BLUE = 3、 GREEN_RED = 10、 GREEN_BLUE = 11

12、typedef关键词的功能是什么?

typedef 的真正意思是给一个已经存在的数据类型(注意:是类型不是变量)取一个别 名,而非定义一个新的数据类型。

13、C语言中 反斜杠(\)有哪些作用?

1.C 语言里以反斜杠(\)表示断行。编译器会将反斜杠剔除掉,跟在反斜杠后面的字符 自动接续到前一行。代码也可以利用 反斜杠换行哦~

2.表示转义字符。

14、‘a’和“a”一样吗?

不一样,‘a’是字符,在内存中占一个字节;“a”是字符串, 在内存中占两个 字节,因为后面还跟一个‘\0’。

15、老生常谈的问题:指针和数组有什么关系?

作者的 回答是:没有屁关系!很解气吧?

作者说:指针就是指针,指针变量在 32 位系统下,永远占 4 个 byte,其值为某一个内存的地址。 指针可以指向任何地方,但是不是任何地方你都能通过这个指针变量访问到。数组就是数组,其大小与元素的类型和个数有关。定义数组时必须指定其元素的类型和个数。数组可以存任何类型的数据,但不能存函数。

虽然的确没有本质上的关系,但我觉得还是要正面解释这个问题,发现有一个解释比较详细,从历史的角度解释了一下指针和数组的爱恨情仇,点这里了解一下

16、数组指针和指针数组的区分

指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身 决定。它是“储存指针的数组”的简称。数组指针:首先它是一个指针,它指向一个数组。在 32 位系统下永远是占 4 个字节, 至于它指向的数组占多少字节,不知道。它是“指向数组的指针”的简称。

那么下面到底哪个是数组指针,哪个是指针数组呢:

A),int *p1[10];

B),int (*p2)[10];

需要弄清楚“[]”和“*”的优先级,“[]”的优先级要比“*”的 优先级高,。p1 先与“[]”结合,构成一个数组的定义,数组名为 p1,int* 修饰的是数组的内容,即数组的每个元素。那现在我们清楚,这是一个数组,其包含 10 个 指向 int 类型数据的指针,即指针数组。至于 p2 就更好理解了,在这里“()”的优先级比 “[]”高, “*”号和 p2 构成一个指针的定义,指针变量名为 p2,int 修饰的是数组的内容, 即数组的每个元素。数组在这里并没有名字,是个匿名数组。那现在我们清楚 p2 是一个指 针,它指向一个包含 10 个 int 类型数据的数组,即数组指针。

17、逗号表达式了解一下?

逗号也是一个运算符,逗号表达式你听过么?

C语言提供一种特殊的运算符:逗号运算符,优先级别最低,它将两个及其以上的式子联接起来,从左往右逐个计算表达式,整个表达式的值为最后一个表达式的值。如:(3+5,6+8)称为逗号表达式,其求解过程先表达式1,后表达式2,整个表达式值是表达式2的值,因此其值是14。

18、二维数组作为参数和二维指针作为参数的写法

二维数组:int arr[3][4],二维指针int **ptr。

二维数组作为参数的写法有两种:

void fun(char (*p)[4]);        // 这里的*p两边的括号不能去掉!因为[]的优先级高
void fun(chara[ ][4]);       // 一维数组括号里的数字可以不写

针对写法2需要注意一点:不作为参数时,在C语言中允许多维数组的第一个维度不声明具体值,但必须在紧跟其后的初始化的一对{}中给明具体的元素。如:int a[][4]={{1,2,3,4},{5,6}}; 这种写法是正确的,但int a[][4];这种写法是错误的。 

19、堆、栈和静态区各存放什么数据?

静态区:保存自动全局变量和静态变量(静态全局和局部变量)。静态区的内容在整个 程序的声明周期内都存在,由编译器在编译的时候分配。

栈:保存局部变量、函数的参数值等。栈里的内容只在函数的范围内存在,当函数运行结束后这些内容会被自动销毁。特点是效率高,但空间大小有限。

堆:由malloc或new分配的内存。生命周期由free和delete决定。在没有释放内存之前一直存在,直到程序结束。特点是使用灵活,空间比较大,但容易出错造成内存泄漏。

20、指针没有指向合法内存什么意思?

像下面这个例子,编译没有问题,但是在运行时会出错。第14行代码将字符串“Tomcat”复制到一个野指针name指向的不合法地址中去。因此在程序运行时调用strcpy函数会导致无权访问出错。解决方法是使用malloc为name指针申请一块内存空间。添加代码:stu.name = (char *)malloc(sizeof(char));在main函数中。

#include <iostream>
#include <string.h>
#include <stdlib.h>
using namespace std;

struct student 
{
	char *name;
	int age;	
} stu, *pstr;
 
int main()
{
	strcpy(stu.name, "Tomcat");
	stu.age = 20;
	cout << stu.name << endl;
	return 0;
}

另外如果要使用pstu时,因为pstu也是指针,需要分配内存, 也需要使用malloc函数给pstu指针分配内存空间。

pstu = (structstudent*)malloc(sizeof(structstudent));

21、内存泄漏了解一下?

malloc和free两个函数配套使用, 一个申请内存,一个释放内存。但是如果对同一个指针malloc两次free一次会造成内存泄漏;malloc一次free两次会出错。使用 free 函数之后指针变量 p 本身保存的地址并没有改变,我们需要手动重新把 p 的值变为 NULL: p = NULL;如果忘记赋值,那么p将会变成野指针!!!

附1:有趣的编程问题一则

下面的程序在运行的时候会出错,解释一下为什么?

void GetMemory(char* p,int num)
{
    p = (char*)malloc(num*sizeof(char));
}

int main()
{
    char *str= NULL;
    GetMemory(str, 10);
    strcpy(str, “Hello”);
    free(str);            //free 并没有起作用,内存泄漏
    return0;
}

这里存在一个很基本的问题:main函数中调用GetMemory函数时传的实参str和GetMemory函数中获得 的形参p是 同一个指针么?当然不是!GetMemory函数得到的只是str的一个副本的地址,因此在GetMemory函数中分配的空间在这个函数执行完毕之后就被释放了,在main函数中的free只能导致内存泄漏。那么如何解决这个问题呢?

方法一:用return

char* GetMemory(char* p,int num)
{
    p = (char*)malloc(num*sizeof(char));
    return p;
}

int main()
{
    char *str= NULL;
    str = GetMemory(str, 10);
    strcpy(str, “Hello”);
    free(str);
    return0;
}

 方法 二:用二级指针!(这个方法我是没想到过的!)

void GetMemory(char **p,int num)
{
    *p = (char*)malloc(num*sizeof(char));
}

int main()
{
    char *str= NULL;
    str = GetMemory(&str, 10);
    strcpy(str, “Hello”);
    free(str);
    return0;
}

 这里的实参是&str即str的地址,是一个值,因此传到函数中的就是其本身而不是副本。而str本身就是一个指向char类型的指针,因此GetMemory函数中的第一个参数为二级指针,在GetMemory函数内部,p即为str的地址&str,*p即为*(&str)也即str,因此malloc所分配的地址就是给str分配的。

附2:另一则有意思的编程题(不使用任何变量编写 strlen 函数)

这个题目的解法肯定是多种多样的,我提供一种容易想到的也容易理解的答案。

intmy_strlen( const char* strDest) 
{ 
    assert(NULL!= strDest); 
    if ('\0' == *strDest) 
    { 
        return 0; 
    } 
    else 
    { 
        return(1 +my_strlen(strDest + 1)); 
    } 
}

猜你喜欢

转载自blog.csdn.net/qq_26822029/article/details/81078526
今日推荐