美文网首页
C 语言学习(5) ---- C语言知识汇总01

C 语言学习(5) ---- C语言知识汇总01

作者: 特立独行的佩奇 | 来源:发表于2022-11-20 18:29 被阅读0次
C语言技巧汇总
元素 说明
常用位运算 对齐,set和reset/reverse,取出相应的位等操作
C语言常量 整数常量,浮点常量,字符常量和字符串常量
const 的用法 TBD
static 的用法 NA
数组相关 数组首地址和赋值问题
结构体相关 结构体定义时赋值
预定义宏 编译器支持的预定义宏
i++ 和 ++i 作为单独语句没有任何区别,区别在于作为右值的时候
大小端 大小端的概念和区别方法
字节对齐 空间换时间的概念
常用位运算

和二进制0与肯定是0,用于清零运算
和二进制1或肯定是1,用于置1运算

使用位运算对齐的形式如下:(包含向前对齐和向后对齐)

#define tAlign(value, base)  value & ~(base - 1)

#define sAlign(value, base)  ((value) + (base - 1))/(base) * base

#define mAlign(value, base)  ((value)+ (base - 1) & (~(base - 1)))

位运算的置0置1,反转BIT运算

#define setbit(x,y) x |= (1 << y)

#define clrbit(x,y) x &= ~(1 << y) 

#define reversebit(x,y) x ^= (1 <<y )

#define getbit(x,y)   (((x)>>(y))&0x01)

获取基本类型中各个8bit 的方法

uint32_t temp = 0x12345678;
printf("test get bits demo \n");
printf("31 ~ 24bits %x \n", (temp & (0xff << 24)) >> 24);
printf("24 ~ 16bits %x \n", (temp & (0xff << 16)) >> 16);
printf("16 ~ 8bits %x \n", (temp & (0xff << 8)) >> 8);
printf("8 ~ 0bits %x \n", (temp & (0xff << 0)) >> 0);
结构体相关
struct komeda_product_data {
    uint32_t product_id;
    uint8_t* ptr;
};

struct komeda_product_data komeda_products = {
    .product_id = 1,
    .ptr = "Hello",
};
数组相关
  • 数组定义时给各个元素赋值
char* err_string[] = {
    [0] = "Zero String",
    [1] = "one String",
    [3] = "Third String",
    [2] = "Fourth String"
};
  • 字符数组的定义方法
    C 语言规定,可以直接将字符串赋值给数组
    char str[] = {"Hello world"}; char str[] = "Hello world";
    要注意的是,一旦定义了,就不能再次赋值了,"hello world" 实际存储在程序的静态存储区

  • 数组地址问题
    定义 char demoarray[ ],则 用demoarray,&demoarray,&demoarray[0] 三个的值本身是一样,区别在于作为指针加1 的时候;

元素 说明
demoarray 和&demoarray[0] 含义是一致的
&demoarray 作为指针每加1 含义是 &demoarray + sizeof(demoarray)
&demoarray[0] 作为数组首元素的首地址,和demoarray 的含义是一致的
void _testArrayAddress() {
    char atestArray[] = { "helloworld"};

    printf("atestArray address is %p \n", atestArray);
    printf("atestArray address is %p \n", &atestArray[0]);
    printf("atestArray address is %p \n", &atestArray);

    char* ptr = atestArray + 1;
    printf("atestArray + 1 address is %p \n", ptr);

    ptr = &atestArray[0] + 1;
    printf("&atestArray[0] + 1 address is %p \n", ptr);

    ptr = &atestArray + 1;
    printf("&atestArray + 1 address is %p \n", ptr);

    // sizeof use atestArray
    printf("sizeof atestArray is %d \n", sizeof(atestArray));
    printf("sizeof atestArray is %d \n", sizeof(&atestArray));
    printf("sizeof atestArray is %d \n", sizeof(&atestArray[0]));

    memset(atestArray, 0, sizeof(atestArray));
    memcpy(atestArray, "demostring", strlen("demostring"));

    printf("after memcpy value is %s \n", atestArray);

}

结果如下:
demoMain Run ....
atestArray address is 00CFF874
atestArray address is 00CFF874
atestArray address is 00CFF874
atestArray + 1 address is 00CFF875 原来地址 + sizeof(char)
&atestArray[0] + 1 address is 00CFF875 原来地址 + sizeof(char)
&atestArray + 1 address is 00CFF87F 原来地址 + sizeof(demoArray)
sizeof atestArray is 11 sizeof 必须使用 demoArray
sizeof atestArray is 4
sizeof atestArray is 4
after memcpy value is demostring

特别注意
memset memcpy memcmp sizeof 都使用数组名就可以,不需要加 & 符号

预定义宏

C语言中预定义宏

__LINE__    行号
__FILE__    文件名
__DATE__    编译日期
__TIME__    编译时间
__STDC__    编译器是否符合C语言那的标准
__FUNCTION__  当前执行的函数
static 的用法

修饰全局变量:代表这个变量只在这个文件内有效
修饰函数:内部的局部变量,此局部变量有全局变量的生命周期
修饰函数:代表这个函数作用域仅限于这个文件,可以不用担心函数重复名称的问题

大小端及其判断方法

现代计算机系统的内存都是按照字节进行划分的,但是存储变量等数据类型的时候存在两种存储方式:
低位字节存储在低位地址这个称为小端模式(little-endian),
低位字节存储在高位地址,这个称为大端模式(big-endian)
举例定位int32_t a = 0x10203040 不同模式下存储模型如下:


align.png

判断系统的大小端代码:
定义一个 int类型,强制转换为 char,并且打印出地址:
使用 union 数据类型实现通用方法

union {        

intc;        

charatemp[4];

} temp_u;

static void testBigLittleEndian3() {        

temp_u.c= 0x10203040;         

if(temp_u.atemp[0] = 0x10 && temp_u.atemp[1] == 0x20)                  

printf("osset as big endian\n");        

elseif(temp_u.atemp[0] = 0x40 && temp_u.atemp[1] == 0x30)                  

printf("osset as little endian\n");        

else                  

printf("unknowos\n");}

字节对齐

现在计算机系统的内存都是按照字节划分的,理论上对任何变量的访问可以从任何地址开始,但是实际情况是访问特定的变量经常在特定的内存地址访问,这就需要各种类型的数据按照一定的规则在空间上进行排列,而不是顺序排放,这就是对齐的概念;
比如在32bit system 下,一个4字节的int,如果它的地址是0x00000004 (4 的倍数),那么它就是对齐的;如果是0x00000002(非4 的倍数),那么它就是非对齐的

  • 为什么要使用字节对齐
    字节对齐的根本原因是在于CPU访问效率的问题,上面的例子中如果地址时0x00000002,那么CPU 需要访问两次内存,
    第一次访问0x000000002 ~ 0x00000003 得到一个2 byte 的内容;
    第二次访问0x000000004 ~ 0x00000005 在得到一个2 byte的内容,组合得到整形数据
    如果变量在对齐位置上,就可以一次取出数据,一些系统对对齐比较严格,比如spar 系统,取未对齐的数据就会发生错误;在x86 上不会产生错误,只是效率下降;不同的对齐方式,在内存中存储的方法可能不一样,合理利用字节对齐,可以有效节约存储空间
  • 空间换取时间
    如果编程的时候考虑节约空间的话,只需要假定结构体的地址为0,然后各个变量根据上面的规则进行排列就可以了,基本的原则就是将结构体中的变量按照从小到大进行声明,尽量减少中间的填补空间;还有一种就是以空间换取时间的效率,显式对空间进行填补,比如下面的做法:
struct A{
    char a;
    charreserve[3];(空间换取时间)
    int b;
}
C 语言中的常量

C语言中的常量分为整数常量,浮点常量,字符常量和字符串常量;

C语言常量类型 说明
整数常量 整数常量可以是十进制、八进制或十六进制的常量,前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制;
浮点常量 浮点常量由整数部分,小数点,小数部分和指数部分组成; 当使用小数形式表示时,必须包含整数部分,小数部分,或同时包含两者;当使用指数形式表示时, 必须包含小数点、指数,或同时包含两者,带符号的指数是用 e 或 E 引入的
字符常量 字符常量可以是一个普通的字符(例如 'x')、一个转义序列(例如 '\t'),或一个通用的字符(例如 '\u02C0')
字符串常量 字符串字面值或常量是括在双引号 " " 中的

转义字符含义表:
\ \ 字符
' ' 字符
" " 字符
? ? 字符
\a 警报铃声
\b 退格键
\f 换页符
\n 换行符
\r 回车
\t 水平制表符
\v 垂直制表符
\ooo 一到三位的八进制数
\xhh . . . 一个或多个数字的十六进制数

C语言中常量的定义形式有两种:

  • 使用 #define 预处理器,
  • 使用 const 关键字

相关文章

网友评论

      本文标题:C 语言学习(5) ---- C语言知识汇总01

      本文链接:https://www.haomeiwen.com/subject/dpgbxdtx.html