c语言补漏

1.寄存器

寄存器是CPU内部用来暂时存放数据/地址/指令的小型存储区域
寄存器距离ALU最近,存储速度最快
数据从内存里拿来先放到寄存器,然后CPU再从寄存器里读取数据来处理,处理完后同样把数据通过寄存器存放到内存里,CPU不直接与内存打交道
registe变量必须是能被CPU寄存器所接受的类型,这意味着register变量必须是一个单个的值,并且其长度应小于等于整型长度,而且register变量可能不存放在内存中,所以不能用取地址操作符&来获取register变量的地址

2.优先级问题

.的优先级高于*
p.f - 对p取f偏移,作为指针,然后进行解引用操作 (p.f) 可用->来解决这一问题
[]的优先级高于

int ap[] - ap是一个指向int数组的指针
函数()高于

int
fp() fp是个函数,返回int*
==和!=操作高于位运算
(val&mask!=0)
val&(mask!=0)
==和!=高于赋值符
c=getchar()!=EOF
c=(getchar()!=EOF)
算术运算符高于位移运算符
msb<<4+lsb
msb<<(4+lsb)
逗号运算符在所有运算符中优先级最低
i=1,2
(i=1),2

3.const

编译器通常不为const只读变量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的值,没有了存储与读内存的操作,使得它的效率也很高
#define M 3
const int N=5;//此时并未将N放入内存中

int i=N;//此时为N分配内存,以后不再分配内存
int I=M;//预编译期间进行宏替换,分配内存
int j=N;//没有内存分配
int J=M;//再进行宏替换,又一次分配内存

const定义的只读变量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的立即数,所以,const定义的只读变量在程序运行过程中只有一份备份(因为它是全局的只读变量,存放在静态区),而#define定义的宏常量在内存中有若干个部分。#define宏是在预编译阶段进行替换,而const修饰的只读变量是在编译期间确定其值。#define宏没有类型,而const修饰的只读变量具有特定的类型

4.无法向函数传递一个数组

在C语言中,所有非数组形式的数据实参均以传值形式(对实参做一份备份并传递给它的那份备份)调用,然而,如果要复制整个数组开销将会非常大,函数的返回值也不能是函数,也只能是指针。函数本身是没有类型的,函数的返回值才有类型

5.((void()()0)()

①.void()()是函数指针,该函数指针指向一个函数,该函数的返回值为void,参数为空
②.(void(
)())0,将0强制转换为函数指针类型,0是一个地址,也就是将一个函数保存在首地址为0的一个区域内
③. ((void()())0) ,这是取0地址开始的一段内存的内容,其内容就是保存在首地址为0的一段区域内的函数
④.((void()())0)()这就是函数调用

6.offsetof宏的实现

#define offsetof(type,name) (size_t)&(((type*)0)->name)
Type是结构体类型名,Name是成员名。具体操作方法是:
1、先将0转换为一个结构体类型的指针,相当于某个结构体的首地址是0。此时,每一个成员的偏移量就成了相对0的偏移量,这样就不需要减去首地址了。
2、对该指针用->访问其成员,并取出地址,由于结构体起始地址为0,此时成员偏移量直接相当于对0的偏移量,所以得到的值直接就是对首地址的偏移量。
3、取出该成员的地址,强转成size_t并打印,就求出了这个偏移量。

7.写代码向内存0x12ff7c地址上存入一个整型数0x100

int*p=(int*)0x12ff7c
*p=0x100;

*(int*)(0x12ff7c)

8.volatile

volatile与const一样是一种类型修饰符,用它修饰的变量表示可以被某些编译器未知的因素修改,比如操作系统、硬件、或者其他线程,遇到这个关键字声明的变量,编译器就不再对其进行优化,从而可以提供对特殊地址更稳定的访问

int i=10;
int j=i;
int k=i;

volatile int i=10;
int j=i;
int k=i;

代码1这时候编译器对代码进行优化,因为在代码1的语句中,i没有被用作左值(没有被赋值),这时候编译器认为i的值没有发生改变,所以在第一条语句从内存中取出i的值赋值给j后,这个值并没有被丢掉,而是在第二条语句继续给k赋值。编译器不会生成出汇编代码重新从内存中取i的值(不会编译生成装载内存的指令,比如ARM的LDM指令),提高效率,但要注意,两条语句之间i没有用作左值(没有被赋值)才行
代码2中,volatile告诉编译器,i是随时可能发生变化的,每次使用它时必须从内存中取出i的值,因而编译器生成的汇编代码会重新从i的地址读取数据放到k内

9.关于static

static int j;
int k=0;
void fun1(void){
static int i=0;
i++;
}
void fun2(void){
j=0;
j++;
}
int main(){
for(k=0;k<10;k++){
fun1();
fun2();
}
}
}

由于被static修饰的局部变量总是存在内存的静态区,所以即使这个函数运行结束,这个静态变量的值还是不会销毁,函数下次使用时仍然能用到这个值,所以,i的值只被初始化一次,而j是全局变量,每次调用函数都初始化

静态全局变量和其他的全局变量的存储地点并没有区别,都是在.data段(已初始化)或者.bss段(未初始化)内,但是它只在定义它的源文件内有效

10.模拟实现atoi

//atoi    将一个字符串转化为一个整型
#include<stdio.h>
#include<assert.h>
#include<limits.h>
#include<ctype.h>
enum state
{
	Value,
	ErrValue
};
state statue = ErrValue;
int my_atoi(const char*str)
{
	assert(str);
 
	char* p = (char*)str;
 
	long long n = 0;
	int flag = 1;
	//字符串长度为0
	if (*str == '\0')
	{
		return 0;
	}
	//字符串前面有很多空格
	while (*p == ' ')
	{
		p++;
	}
	//判断符号
	if (*p == '+')
	{
		flag = 1;
		p++;
	}
	else if (*p == '-')
	{
		flag = -1;
		p++;
	}
	//开始计算
	while (*p!='\0')
	{
		if (isdigit(*p))
		{
			n = n * 10 + flag * (*p - '0');
			if (n >INT_MAX)
			{
				n = INT_MAX;
				break;
			}
			else if (n < INT_MIN)
			{
				n = INT_MIN;
				break;
			}
		}
		else
		{
			break;
		}
		p++;
	}
 
	if (*p == '\0')
	{
		statue = Value;
	}
 
	return (int)n;
}/**
 特殊情况
 1前面有一大堆空格
 2传过来空指针
 3字符串长度为0
 4整形溢出
 5含有其它字符
 */
int main()
{
	char arr[] = "12345678";
	int m = my_atoi(arr);
	if (statue == Value)
	{
		printf("%d\n", m);
	}
	if (statue == ErrValue)
	{
		printf("非法转化\n");
	}
 
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值