C开发基础

326 阅读4分钟

在实际的开发过程中,经常遇到 以下几个问题

大小端

举个例子:通俗易懂的描述是

大端就是符合人类阅读习惯的 。从左到右,高位放在 低地址 ,低位放在高地址

小端是符合 机器阅读习惯的。从右到左,高位放在高地址,低位放在低地址。

0x123456 理解成 16进制的 个十百, 我们正常写一个数字 123 代表 一百二十三 从左到右 1代表高位【百位】 3代表低位【个位】存放的位置决定了 大端还是小端的模式。而内存地址是从低到高的从左到右的 所以 如果 123 直接 存到 内存里面 即 1存放在了 内存的 低地址 3 存放在了内存的高地址就是 大端模式,如果反过来 1存放在 内存的 高地址 3存放到 内存的低地址 就是小端模式。

#include <stdio.h>

int main(int argc,char* argv[]){
    char test[2]={0};
    test[0] = 0x10;
    test[1] = 0x24;
    short* tests = (short*)test;
    printf("0x%x\n",*tests);
    
    long testBIG = 0x011FFFFFFFFFFFF0;
    printf("0x%x%x\n",*((int32_t*)&testBIG+1),testBIG);
    printf("0x%lx\n",testBIG);
}
//输出为 : 0x2410 这就是小端

解释以下: 0x1024 代表的是 10 代表高位 ,可以理解成 数学里面的十进制的十位数。24 就是代表个位数。

低地址放在低位,高地址放高位,所谓的低地址就是 索引号低的位置 就是 0 ,索引越高地址越高。

相当于 [0] 存放了0x24 ,[1] 存放了 0x10。

以下为大小端互转

#define sw16(a) ((a&0xFF<<8)|(a&0xFF00>>8))
#define sw16(x)\
&emsp;((short)(\
&emsp;(((short)(x)&(short)0x00ffU)<<8)|\
&emsp;(((short)(x)&(short)0xff00U)>>8)))

用处:pcm经常采用 16k 16位 单声道采集。而 16位 就衍生出大端存储 和小端存储 LE BE 你在做转换的时候就会需要用到哦。

反过来:

    short test[2] ={0};
    test[0] = 0x1234;//保存在内存里的位置 再大端模式下 低地址存放低位 即34 高地址存放高位即12
    test[1] = 0x5678;//同上
    char* tests = (char*)test;//12345678 
    printf("0x%x%x%x%x\n",*tests ,*(tests+1),*(tests+2),*(tests+3));
//输出 34127856

例子:网络序

参考: 这里

字节对齐

再做音频解码也好,其他的处理也好,如果遇到 16位 以上的都要留心,是否需要做字节对齐。

这里面涉及简单的算法。

如果是16位的数据, 则需要 注意 处理的是奇数还是偶数,如果是奇数,则存在 对齐。

void process (char con[2]){
    printf("0x%x%x\n",con[0],con[1]);
}

char alignData ='\0';
void align(char * data ,size_t len){
    if(alignData !='\0'){
        char temp [2] ={0};
        memcpy(temp,&alignData,1);
        memcpy(temp+1,data,1);
        process(temp);
        data +=1;
        --len;
    }
    for(int i = len-1;i>=1;i=i-2){
        process(data);
        data = data+2;
    }
    if(len%2==1){
        alignData = (data[len-1]);
    }else{
        alignData ='\0';
    }
}
void main()
{
    int testalign = 0x12345678;
    align(((char*)&testalign),1);
    align(((char*)&testalign)+1,3);
}
// 打印
//0x7856
//0x3412

也许你看不出来,在上述中的作用,但是其实可以将process 弄得更复杂一点比方说: 第一个字节表示 左声道,第一个字节表示 大端序等等, 总之顺序是不能更换的,这时候,就凉凉了。所以字节对齐是很重要的。

写文件

在平时写文件的时候,其实只要 fopen 即可。但是现在存在这样的一种情况。往挂载文件中写文件,但是还未挂载,比方说

mount /dev/sdb1 /newdisk 

这时 /newdisk 这个目录是存在的,所以此时你往这里面写入是可以成功的,即使没有挂载成功。

顺带了解了以下,原来 fopen 是由缓存的。原文连接 在这里

fopen vs open in C

1) fopen is a library function while open is a system call.

2) fopen provides buffered IO which is faster compare to open which is non buffered.

3) fopen is portable while open not portable (open is environment specific).

4) fopen returns a pointer to a FILE structure(FILE *); open returns an integer that identifies the file.

5) A FILE * gives you the ability to use fscanf and other stdio functions.

这里提供两种写文件的方式吧。

open 和 fopen 的方式

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv)
{
    int filedesc = open("testfile.txt", O_WRONLY | O_APPEND);//替换成O_CREAT即可
    if (filedesc < 0)
    {
        write(2,"error open \n",strlen("error open \n"));
        return 1;
    }

    if (write(filedesc, "This will be output to testfile.txt\n", 36) != 36)
    {
        write(2, "There was an error writing to testfile.txt\n", strlen("There was an error writing to testfile.txt")); 
        return 1;
    }
}

fopen版

void testfopen(){
    FILE* file = fopen("a.txt","w+");
    if(!file){
        printf("error\n");
    }
    //判断文件是否存在
    if(access("a.txt",F_OK)==0){
        printf("存在\n");
    }
    // 写入文件
    char buf[20]="hello world\0";
    fwrite(buf,1,strlen(buf),file);
    fclose(file);
}

通过上面可以看出 fopen 或者 open之后文件就会被立刻创建。不用等到 writeclose 才会被创建。

顺带说下: 判断文件是否存在 使用 access , strlen\0 则结束.