C语言-易错点汇总

news2024/11/25 14:19:43

易错点汇总

    • 指针数组和数组指针
    • (*p).a
    • struct stdent和typedef struct stdent
    • F5和Ctrl+F5
    • const的位置
    • 全局变量和局部变量
    • 两个相同的常量字符串
    • 数组名和&数组名
    • 数组指针定义的解读
    • int (*arr[10])[5]
    • 数组传参
    • (* (void (*)())0)();
    • 关于数组名是否表示整个数组和数组首元素的地址
    • 结构体内存对齐
    • 空指针和野指针
    • 函数传参问题
    • 直接打印字符串的首地址,可打出字符串
    • 函数内部数组和指针中存储的常量字符串
    • 预定义#define和函数下的计算max的两个带有副作用的例子
    • unsigned char的加减问题
    • 二进制奇数位和偶数位的互换
    • *p[1] + 3 和 *(p[1] + 3)
    • 连续输入两个带空格的字符串(scanf,gets,getchar)
    • 其它


指针数组和数组指针

指针数组是一个数组。如同 int arr[10],表示arr数组有10个元素,每一个元素都是整型
指针数组 int* arr[10],表示arr数组有10个元素,每一个元素都是指针;

数组指针是指针。int (*arr)[10],arr先与 *结合,说明arr是一个指针,然后指向一个大小为10个整型的数组。注意:[]的优先级要高于*,所以必须加上()来保证arr与*结合。


(*p).a

#include < stdio.h >
struct S
{ 
  int a;
  int b; 
};
int main( )
{ 
  struct S a, *p=&a;
  a.a = 99;
  printf( "%d\n", __________);
  return 0;
}

*p.a 这种表示方式是错误的,.的优先级要高于*,那么就是先要看p.a,而p是一个指针,指针是不能使用.的操作的
(*p).a (*p)表示结构体变量p,.a表示p中的成员a


struct stdent和typedef struct stdent

struct stdent
{
	char name[20];
	char sex[10];
	int age[5];
}stu;
//stu是结构体变量

typedef struct stdent
{
	char name[20];
	char sex[10];
	int age[5];
}stu;
//stu是类型

注意:typedef是为一个类型起一个新的名字


F5和Ctrl+F5

F5是开始调试,在遇到短点的位置可以停下来;
Ctrl+F5是开始执行,不调试


const的位置

一个变量被const修饰,那么它的值就不能被改变;

const type name = value;

const和type的位置是可以互换的,也即是type const name = value;
比如:const char *p和char const *p是一个意思,p所指向的内容不可变,但p本身可变。


全局变量和局部变量

全局变量未初始化,默认值为0;局部变量未初始化,默认值为随机值;


两个相同的常量字符串

由const修饰的常量字符串不能被修改(即使没有const,也是不能被修改的),当创建两个相同的常量字符串的时候,在内存中只创建了一份。也就是说,当两个不同的指针指向这两个相同的字符串的时候,他们所指向的是同一个地址。如下:

	const char* str3 = "hello bit.";
	const char* str4 = "hello bit.";

str3和str4的值是相同的,存放的是同一个地址。


数组名和&数组名

数组名是首元素的地址,&数组名是数组的地址
数组首元素的地址和数组的地址从值的大小来看,是相等的,但是,他们的意义不同。
arr+1,是该数组内第二个元素的地址;&arr+1,是跳过了整个数组后的地址。


数组指针定义的解读

int (*p)[5]   首先是一个指针,这个指针指向一个数组[],
该数组内由5个元素,指针名字为p,又不想p与[5]结合,
所以*p需要加括号,这个数组内每一个元素都是整型

int (*arr[10])[5]

这是一个数组,因为,先看*arr[10],*没有被圈起来,说明arr先与[]结合,
说明是一个数组,然后把arr[10]拿掉,变成了int (*)[5],
说明数组里的每一个元素都是数组指针,指针指向一个数组,
该数组内有5个元素,每一个元素都是整型,示意图如下:

在这里插入图片描述


数组传参

数组传参,形参的部分可以是数组,可以不指定大小,类型交代清楚就可以,如下:

void test(int arr[])
{}
void test(int arr[10])
{}

(* (void (*)())0)();

(*(void (*)())0)();
void (*p)(),这是一个函数指针,去掉p后,就变成了函数指针的类型,括号
里面是一个类型,后面又有数据0,把0强制转换为函数指针类型,把0当作一个函数的地址
,前面又有一个*号,对函数地址解引用操作,为函数,函数后面又有一个小括号,表明为无参


void (*signal(int, void(*)(int)))(int);
首先signal是一个函数名,这个函数有两个形参,一个是整型int,另一个是函数指针
类型,该函数指针指向的那一个函数的形参是整型,函数返回的类型为void
而singal函数的返回值又作为了一个函数指针,该指针指向的那一个函数的参数为整型,
返回类型为void

关于数组名是否表示整个数组和数组首元素的地址

1.sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小
2.&数组名,数组名表示整个数组,取出的是整个数组的地址
除此之外,所有的数组名都是数组首元素的地址

int a[5]={1,2,3,4,5};
sizeof(*&a)等价于sizeof(a)=5*4=20,这里的a表示整个数组
在sizeof之外,*&a=a,表示数组的首地址 

结构体内存对齐

结构体内存对齐,成员变量空间的开辟
1.结构体的第一个成员对齐到结构体在内存中存放位置的0偏移处,占几个字节的空间,就给几个字节的空间
2.从第二个成员开始,每个成员都要对齐到一个对齐数的整数倍处
对齐数:结构体成员自身大小和默认对齐数的较小值
VS:默认对齐数为8
linux gcc :没有默认对齐数,对齐数就是成员自身的大小
3.结构体的总大小必须是所有成员(包含嵌套的结构体成员中的对齐数)的对齐数中最大对齐数的整数倍
4.如果结构体中嵌套了结构体变量,要将嵌套的结构体变量对齐到自己成员中最大对齐数的整数倍处


空指针和野指针

对零位置处的位置进行访问,空指针所指向的位置处
空指针不会指向任何地方,即它不指向任何数据,但是源码中的空指针的常量是整数0,使得指针指向0位置处
但是,大多数系统都将0作为不被使用的地址
空指针和野指针 空指针不等于未初始化的指针,未初始化的指针通常是指野指针,野指针可以指向任何地方,乱指,可能会造成非法访问内存地址,而空指针不指向任何对象


函数传参问题

void GetMemory(char* p)
//char* p 说明与str的类型相同,说明此处进行的是值传递
//如果此处要进行地址传递的话,应该是GetMemory(&str)  void GetMemory(char** p)

//它与int a &a 是一样的,只不过这里的变量不再是普通的变量,而是一个指针变量
//GetMemory(a)  void GetMemory(int b)
//GetMemory(&a) void GetMemory(int* b)
{
	p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}
int main()
{
	Test();
	return 0;
}

直接打印字符串的首地址,可打出字符串

	char* p = "hehe\n";
	printf("hehe\n");
	printf(p);//直接打印字符串的首地址

函数内部数组和指针中存储的常量字符串

char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
//p是一个局部变量,出了函数就会被销毁
char* GetMemory(void)
{
	char* p = "hello world";
	return p;
}
//这里的p指向的是一个常量字符串,出了函数,它仍然存在
void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}
int main()
{
	Test();
	return 0;
}

预定义#define和函数下的计算max的两个带有副作用的例子

关于预定义#define和函数下的计算max的两个带有副作用的例子

#define MAX(x,y) (x)>(y)?(x):(y)

int main()
{
	int a = 4;
	int b = 6;
	int m = MAX(a++, b++);
	//(a++)>(b++)?(a++):(b++)
	//后置++,对于(a++)>(b++),先比较。4>6,不成立,变为(5)>(7)
	//条件表达式,条件不成立,直接跳至(b++),此时b=7,将7作为结果赋值给m
	//之后6++,变为8,也即是b=8,?(a++),这部分没有执行,a=5, m=7
	printf("%d",m);
	return 0;
}

在这里插入图片描述

int MAX(int x,int y)
{
	return x > y ? x : y;
}
int main()
{
	int a = 4;
	int b = 6;
	int m = MAX(a++, b++);
	//会将a=4,b=6带入MAX函数,之后a++,a=5,b++,b=7.
	//那么x=4,y=6,return 带回的返回值为6,于是m=6
	printf("%d\n",m);
	printf("%d %d\n", a, b);
	return 0;
}

在这里插入图片描述


unsigned char的加减问题

int main()
{
	unsigned char i = 7;   //unsigned char 1111 1111  取值范围0-255   char -128~+128
	int j = 0;
	for (;i>0;i-=3) //这里不成功,只有i=0;
	{
		++j;
	}
	printf("%d\n",j);
	return 0;
}

7 4 1 -2? X
对于unsigned char 1-1=0 1-2=255 再减一为254
7 4 1 254 251… 5 2 255 252 249… 0
3 84 1 85 1 173

在这里插入图片描述

int main()
{
 int a = -3;
 unsigned int b = 2;
 long c = a + b;//内存中补码相加
 printf("%ld\n",c);
 return 0;
}

-310000000 00000000 00000000 0000001111111111 11111111 11111111 1111110011111111 11111111 11111111 11111101

2 整数的原,反,补相同
00000000 00000000 00000000 00000010
相加的结果,在内存中,是补码
11111111 11111111 11111111 11111111
由补码换算为原码(规则同原码到补码)
10000000 00000000 00000000 00000000
10000000 00000000 00000000 00000001  -1

二进制奇数位和偶数位的互换

二进制奇数位和偶数位的互换:
奇数位拿出来,向左移动一位;偶数位拿出来,向右移动一位
奇数位与1与,偶数位和0与,保留奇数位,之后整体左移一位。偶数位同理
0110
0101
0100
1000
0110
1010
0010
0001
1000
0001
1001
一个整型4字节,32位
01010101
0x55555555
10101010
0xaaaaaaaa

#define SWAP_BIT(x) x=(((x&0x55555555)<<1)+((x&0xaaaaaaaa)>>1))

int main()
 {
	 int a = 10;
	 SWAP_BIT(a);
	 printf("%d\n",a);
	 return 0;
 }

*p[1] + 3 和 *(p[1] + 3)

int main()
{
	char* p[] = { "shanghai","Beijing","honkong" };
	printf("%c\n", *p[1] + 3);
	printf("%c\n", *(p[1] + 3));
	return 0;
}

在这里插入图片描述
char* p[] 是一个指针数组,该数组内有4个元素,每一个元素都是指针类型。
*p[1] + 3,找到该数组的第一个元素,它是一个地址,对它解引用,得到该地址的第一个元素B,B+3=E
*(p[1] + 3),找到该数组的第一个元素,它是一个地址,也是首元素的地址,即是B的地址,地址+3,也就是j的地址,对它解引用,得到j


连续输入两个带空格的字符串(scanf,gets,getchar)

scanf
gets
getchar
连续输入两个带空格的字符串
1 scanf

int main()
{
	char arr1[20] = { 0 };
	char arr2[20] = { 0 };

	scanf("%[^\n]", arr1);//读到\n处,停止读取
	getchar();//吞掉缓冲区的\n
	scanf("%[^\n]",arr2);

	printf("%s\n", arr1);
	printf("%s\n",arr2);
	return 0;
}

在这里插入图片描述
2 gets:可以看到gets函数有警告提示,但是不影响结果。

int main()
{
	char arr1[20] = { 0 };
	char arr2[20] = { 0 };

	//scanf("%[^\n]", arr1);//读到\n处,停止读取
	//getchar();//吞掉缓冲区的\n
	//scanf("%[^\n]",arr2);

	gets(arr1);
	gets(arr2);

	printf("%s\n", arr1);
	printf("%s\n",arr2);
	return 0;
}

在这里插入图片描述
3 getchar

int main()
{
	char arr1[20] = { 0 };
	char arr2[20] = { 0 };

	//scanf("%[^\n]", arr1);//读到\n处,停止读取
	//getchar();//吞掉缓冲区的\n
	//scanf("%[^\n]",arr2);

	//gets(arr1);
	//gets(arr2);

	char ch = 0;
	int i = 0;
	while ((ch=getchar())!='\n')
	{
		arr1[i++] = ch;
	}

	int j = 0;
	while ((ch = getchar() )!= '\n')
	{
		arr2[j++] = ch;
	}

	printf("%s\n", arr1);
	printf("%s\n",arr2);
	return 0;
}

在这里插入图片描述

其它


switch语句中可以没有default关键字


c程序的基本组成单位是函数


c语言本身是没有输入输出语句的。这是因为 C语言是一门计算机语言,它规定的是语法,按照怎样的规则去写代码,c语言的编译器会对代码进行编译。早期的时候,由于一些功能被经常使用,比如说,求字符串的长度,从界面输入,从界面输出。如果每个人都要自己实现的话,比较费时费力,并且代码不够规范。于是,便引入了标准库,里面的库函数可以实现一些常用的功能。是标准库实现的输入输出语句。


++的优先级要高于*的优先级


对于浮点型,如5.5,它的二进制表示方式为101.1,小数点后的权重依次为2的负1次方,2的负2次方…

IEEE 754规定,任意一个二进制浮点数V,可表示为(-1)^S * M * 2^E 比如:5.5,它的二进制表示方式为101.1,那么它可以写成1.011*2^2
(可以类比10进制的科学计数法101.1=1.011 * 10^2),进一步写成(-1) ^0 * 1.011 * 2^2


有些浮点数在内存中是无法保存的,或者说是无法精确保存的,对于float型小数点后的二进制最多保存23位,对于double型小数点后的二进制最多保存52位


当指针所指向的内存已经被释放,所以其它代码有机会改写其中的内容,相当于该指针从此指向了自己无法控制的地方,也称为野指针;
为了避免失误,最好在free之后,将指针指向NULL。


数据总线为32位,意味着一次读取32个bit位,也就是4个字节


解决头文件被重复包含的问题 条件编译


i[arr] 等价于 *(i+arr) 等价于 *(arr + i) 等价于 arr[i]


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/534561.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java位运算

Java >>和>>>的区别 | 或&#xff1a; 有1得1&#xff0c; 全0得0 int temp a|c;System.out.println(Integer.toBinaryString(a));System.out.println(Integer.toBinaryString(c));System.out.println(Integer.toBinaryString(temp));/*** 结果输出* * 01* …

对比SQL学Python:筛选|条件判断

SQL里筛选数据主要用到了where、and、in等语句。而在Python里实现筛选的方式比较多样&#xff0c;用到了 与&或|非~ 逻辑运算符&#xff0c;和isin()函数等。我们感受一下二者的区别吧&#xff1a; 汇总&#xff1a; 类型Python语句参考单条件筛选 data[data[shop_type]A…

Python复制目录及其子目录下的所有文件到指定新目录并重命名

Python复制目录及其子目录下的所有文件到指定新目录并重命名 前言前提条件相关介绍实验环境Python复制目录及其子目录下的所有文件到指定新目录并重命名代码实现输出结果 前言 本文是个人使用Python处理文件的电子笔记&#xff0c;由于水平有限&#xff0c;难免出现错漏&#x…

最常用的界线矢量数据大合集(文末有附下载方法)

最近收集了挺多比较常用的矢量界线数据&#xff0c;例如地理分区、气候分区等界线数据&#xff0c;在日常制图、学习、科研等方面使用频率比较高。废话不多说&#xff0c;这里给大家分享一下&#xff01;&#xff01; 1、中国农业熟制区划矢量数据 2、黄土高原空间范围矢量数据…

【Leetcode -509.斐波那契数 -520.检测大写字母】

Leetcode Leetcode - 509.斐波那契数Leetcode - 520.检测大写字母 Leetcode - 509.斐波那契数 题目&#xff1a;斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 。 该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。…

node笔记_koa框架是什么?

文章目录 ⭐前言⭐ koa框架是如何发展而来的&#xff1f;⭐ koa框架的基本使用&#x1f496; 安装 koa&#x1f496; koa的Middleware示例&#x1f496; 支持xml ⭐ 结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文介绍node的一个web框架koa。 往期文章 node_window…

第三章: Mybatis-Plus 的通用CRUD API 练习使用

目录 1. Insert 操作 -> Create 1.1: TableId 的 id策略 1.2: TableField 2. Update 操作 -> Update 2.1: 根据 ID 更新 2.2: 根据条件更新 3. Delete 操作 -> Delete 3.1: deleteById 3.2: deleteByMap 3.3: delete 3.4: deleteBatchIds 4. Select 操作 -&g…

Redis修炼 (15. redis的持久化-RDB)

RDB 就是 redis database backup file 数据备份文件 就是把内存中的所有数据都保存在磁盘中。 save 注意这个保存过程是主进程在做 因为redis 是单线程 所有其他所有请求都会被卡死。 bgsave 这个稍微友好一点 是子进程 执行&#xff0c;避免主进程收到影响。 redis在服务停机…

【JavaWeb】-- Maven基础、MyBatis

文章目录 Maven基础1.Maven简介1.1 Maven模型1.2 仓库 2.Maven安装3.Maven基本使用3.1 Maven常用命令3.2 Maven生命周期 4.IDEA使用Maven4.1 IDEA配置Maven环境4.2 Maven坐标4.3 IDEA 创建Maven项目 5.依赖管理5.1 使用坐标引入jar包5.2 依赖范围 MyBatis1.MyBatis概述1.1JDBC的…

JDK、JRE和JVM三者的区别和联系

一、JDK、JRE、JVM分别是什么 &#xff08;一&#xff09;JDK JDK&#xff0c;全称Java Development Kit&#xff0c;是 Java 语言的软件开发工具包&#xff0c;主要用于移动设备、嵌入式设备上的Java应用程序。JDK是整个Java开发的核心。 &#xff08;二&#xff09;JRE J…

Day970.数据库表解耦 -遗留系统现代化实战

数据库表解耦 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于数据库表解耦的内容。 微服务拆分之初&#xff0c;需要搭建好的两个基础设施&#xff0c;一个是基于开关的反向代理&#xff0c;另一个是数据同步机制。 有了这两个设施做保障&#xff0c;接下来就可以…

Python合并同名Labelme标注文件内容

Python合并同名Labelme标注文件内容 前言前提条件相关介绍实验环境Python合并同名Labelme标注文件内容Json文件代码实现输出结果json文件 前言 本文是个人使用Python处理文件的电子笔记&#xff0c;由于水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。 (https://b…

Sping核心知识点总结

Spring框架日渐成熟&#xff0c;已经成为java开发中比不可少的部分&#xff0c;框架这东西我的理解里属于工具型应用&#xff0c;意味着如果没有大量实践之前之间研究理论 研究源码之类的 体会会很效率会很低&#xff0c;所以个人建议萌新先找个项目做一做&#xff0c;感受一下…

【C/C++的内存管理】

欢迎阅读本篇文章 前言&#x1f355;1. C/C内存分布1.1有关C/C的一道题目 &#x1f355;2. C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free&#x1f355;3. C内存管理方式3.1 new/delete操作内置类型3.2 new和delete操作自定义类型 &#x1f355;4. operator …

论国内如何免费使用GPT4

什么是GPT&#xff0c;能做什么&#xff1f; GPT&#xff0c;全名为Generative Pre-trained Transformer&#xff0c;是一类基于Transformer架构的自然语言处理模型。GPT的主要功能包括&#xff1a; 文本生成&#xff1a;能够根据给定的输入生成合理的文本&#xff0c;如文章、…

双向链表实现约瑟夫问题

title: 双向链表实现约瑟夫问题 date: 2023-05-16 11:42:26 tags: **问题&#xff1a;**知n个人围坐在一张圆桌周围。从编号为k的人开始报数&#xff0c;数到m的那个人出列&#xff1b;他的下一个人又从1开始报数&#xff0c;数到m的那个人又出列&#xff1b;依此规律重复下去&…

Java进阶-Collection集合

1.Collection集合 1.1数组和集合的区别 相同点 都是容器&#xff0c;可以存储多个数据 不同点 数组的长度是不可变的&#xff0c;集合的长度是可变的 数组可以存基本数据类型和引用数据类型 集合只能存引用数据类型&#xff0c;如果要存基本数据类型&#xff0c;需要存对应的…

ubuntu20.04开机界面黑屏,只有一个光标闪烁

接下来我就把我的解决方法完整的发出来&#xff0c;因为我也是非常的绝望&#xff0c;终于在不断尝试中解决了问题 首先开机界面就是这个东西&#xff0c;一直卡在这不动了&#xff0c;原因就是&#xff0c;内存被用完了&#xff0c;无法加载出图形化界面 解决方法&#xff1…

springboot基于vue的MOBA类游戏攻略分享平台

系统分析 系统可行性分析 1、经济可行性 由于本系统本身存在一些技术层面的缺陷&#xff0c;并不能直接用于商业用途&#xff0c;只想要通过该系统的开发提高自身学术水平&#xff0c;不需要特定服务器等额外花费。所有创造及工作过程仅需在个人电脑上就能实现&#xff0c;使…

Redis学习--下载与安装

Redis下载与安装 Redis安装包分为windows版和Linux版&#xff1a; Windows版下载地址&#xff1a;https://github.com/microsoftarchive/redis/releases Linux版下载地址&#xff1a;https:/download.redis.io/releases 在Linux系统安装Redis步骤&#xff1a; 1.将Redis安装…