C项目开发常见问题集合

1、如何给结构体中的字符数组赋值

在了解给结构体字符数组赋值之前,先了解一下字符数组这个东西
例如:

(1)定义并对字符数组初始化

定义一个字符数组,不赋值初始化:

char	a[10];

定义一个字符数组,并初始化:

char	a[10] = "hello";

定义一个字符数组,对每个元素逐个赋值:

char	a[10] = {'h', 'e', 'l', 'l', 'o'};

上面三种方法都是正确的,但是我们很多时候会像下面这样,先定义再赋值,但是这是错误的,编译系统也会报错,例如:

错误1:

char	a[10];
a[10] = "hello";

编译提示:
在这里插入图片描述
原因:a[10] = “hello”;表达式中,欲将"hello"字符串的首地址赋值给a[10],而字符数组a每个元素存储的是字符,就是ASII码对应的数值,也就是整数,这就是为什么提示,将一个指针赋值给一个整数没有强制转换。而且a[10]只是数组a的一个元素,一个字符怎么能容纳一个字符串?明显不对。

错误2:

char	a[10];
a = "hello";

原因:这种情况也是很离谱的,而且不容易发现。在执行第一条语句,char a[10];表达式的时候,系统开辟的一段内存给了字符数组a[10], 所以一开始a代表的是字符数组a[10]内存空间的首地址,即a指向字符数组a[10]的首地址;执行第二条表达式a = “hello”;后,a不再指向字符数组a[10]内存空间的首地址,而是字符串常量"hello"的首地址,这时指针a出现指向混乱,不允许!

但是如果一开始定义了一个字符数组,并没有初始化,后面用到的时候再赋值,那应该怎么办呢?
可以利用strcpy()和strncpy()函数:

char	a[10];
strcpy(a,"hello");

(2)如何对结构体的字符数组赋值

其实就是用上面的strcpy()和strncpy()函数

C 库函数 strcpy()

char *strcpy(char *dest, const char *src) 

把 src 所指向的字符串复制到 dest。需要注意的是如果目标数组 dest 不够大,而源字符串的长度又太长,可能会造成缓冲溢出的情况.

C 库函数strncpy()

 char *strncpy(char *dest, const char *src, size_t n) 

把 src 所指向的字符串复制到 dest,最多复制 n 个字符。当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。
实例:

#include <stdio.h>
#include <string.h>

typedef struct student_s{
    char name[64];
    int grade;
}student_t;

int main()
{
    student_t student;

    memset(&student, 0, sizeof(student));
    //strcpy(student.name, "zcx");
    strncpy(student.name, "zcx", strlen("zcx"));
    student.grade = 99;

    printf("name:%s, grade:%d, strlen(\"zcx\"):%ld\n", student.name, student.grade, strlen("zcx"));

    return 0; 
}

2、strlen和sizeof求字符串长度的区别

1.sizeof会将空字符\0计算在内,而strlen不会将空字符\0计算在内。

2.sizeof是操作符(关键字),它结果类型是size_t,它在头文件中typedef为unsigned_int类型。 strlen是函数。

3.sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以’‘\0’'结尾的。
4.数组做sizeof的参数不退化,传递给strlen就退化为指针了。

sizeof()的功能是计算一个数据类型的大小,这个类型可以是数组、函数、指针、对象等,单位为字节,它的返回值是size_t类型,也就是unsigned int类型,是一个无符号整数。注意:sizeof不是一个函数,它是一个运算符,所以它不需要包含任何头文件。

strlen()的功能是计算一个指定字符串的长度,函数原型如下:

size_t	strlen(const char *s)

它的返回值是size_t类型,也就是unsigned int类型,返回的是字符串的长度,需要包含头文件#inlude <string.h>,参数s是字符串首地址。

size_t:size_t 是为了方便系统之间的移植而定义的,在32位系统上定义为 unsigned int,在64位系统上定义为 unsigned long int。

3、字符串转换为int或float

atoi 是把字符串转换成int型的函数,其函数参数是一个字符串,如果字符串的的开始字符为空格则跳过再做转换,如果第一个非空格字符存在,是数字或者正负号则开始做类型转换,之后检测到非数字(包括结束符 \0和.) 字符时停止转换,返回整型数。否则,返回零。

#include <stdlib.h>
#include <stdio.h>
 
int main(void)
{
    int i1, i2;
    float f1, f2;
    char* str1 = "25.5";//注意:atoi和atof遇到.就停止往后转换了
    char* str2 = "25.5a";

    i1 = atoi(str1);
    i2 = atoi(str2);

    f1 = atoi(str1);
    f2 = atoi(str2);

    printf("str1=%s,str2= %s\n",str1, str2);
    printf("i1=%d, i2=%d\n", i1, i2);
    printf("f1=%f, f2=%f\n", f1, f2);
    
    return 0;
}

运行结果:

tr1=25.5,str2= 25.5a
i1=25, i2=25
f1=25.000000, f2=25.000000

我们将代码改一下,在字符串面前加上空格和正负号,再运行:

#include <stdlib.h>
#include <stdio.h>
 
int main(void)
{
    int i1, i2;
    float f1, f2;
    char* str1 = "     25.5";
    char* str2 = "-25.5a";

    i1 = atoi(str1);
    i2 = atoi(str2);

    f1 = atoi(str1);
    f2 = atoi(str2);

    printf("str1=%s,str2= %s\n",str1, str2);
    printf("i1=%d, i2=%d\n", i1, i2);
    printf("f1=%f, f2=%f\n", f1, f2);

    return 0;
}

运行结果:

str1=     25.5,str2= -25.5a
i1=25, i2=-25
f1=25.000000, f2=-25.000000

4、stat()函数

头文件:
#include <sys/stat.h>
#include <unistd.h>
函数原型:

int stat(const char *file_name, struct stat *buf)

参数:通过文件名filename获取文件信息,并保存在buf所指的结构体;
stat函数中的struct stat结构体成员:

struct stat {
dev_t         st_dev;       //文件的设备编号
ino_t         st_ino;       //节点
mode_t        st_mode;      //文件的类型和存取的权限
nlink_t       st_nlink;     //连到该文件的硬连接数目,刚建立的文件值为1
uid_t         st_uid;       //用户ID
gid_t         st_gid;       //组ID
dev_t         st_rdev;      //(设备类型)若此文件为设备文件,则为其设备编号
off_t         st_size;      //文件字节数(文件大小)
unsigned long st_blksize;   //块大小(文件系统的I/O 缓冲区大小)
unsigned long st_blocks;    //块数
time_t        st_atime;     //最后一次访问时间
time_t        st_mtime;     //指最近修改文件内容的时间
time_t        st_ctime;     //指最近改动Inoed的时间
};

5、chdir()

头文件:#include <unistd.h>

函数原型:

int chdir(const char * path);

函数说明:chdir()用户将当前的工作目录改变成以参数路径所指的目录。
返回值执行成功则返回0,失败返回-1,errno为错误代码。

6、dirname()

dirname() 函数返回路径中的目录名称部分。
函数原型:

char *dirname(path);

basename作用是得到特定的路径中的最后一个’/',后面的内容
如/usr/bin,得到的内容就是bin
如果/sdcard/miui_recovery/backup
得到的内容就是backup
如果是/sdcard/update.zip
得到的内容就是update.zip

7、snprintf()

其返回值为欲写入第一个参数的字符串长度
在这里插入图片描述
需要注意的是:使用snprintf将一个字符串写入一个字符数组后,如果又想重新写入另外一个字符串,我们不用memset来清空字符数组,因为在每写入一个字符串的时候,snprintf会自动在后面加一个"\0",作为字符串的结束标志,所以我们读出来的时候,也就是读到我们最新写入的字符串而已,不存在字符串连接在一起的情况。

测试实例:

//test.c
#include <stdio.h>
 
int main()
{
    char buffer[50];
    char* s1 = "runoobcom";
 
    // 读取字符串并存储在 buffer 中
    int j1 = snprintf(buffer, 50, "%s\n", s1);
 
    // 输出 buffer及字符数
    printf("string:%s\ncharacter count = %d\n", buffer, j1);
 
    char* s2 = "hello";

    // 读取字符串并存储在 buffer 中
    int j2 = snprintf(buffer, 50, "%s\n", s2);
 
    // 输出 buffer及字符数
    printf("string:%s\ncharacter count = %d\n", buffer, j2);

    return 0;
}

运行结果:
在这里插入图片描述

8、/dev/null设备文件

/dev/null代表linux的空设备文件,所有往这个文件里面写入的内容都会丢失,俗称“黑洞”

9、getdtablesize()

#include <unistd.h>

int getdtablesize(void);

描述getdtablesize() 返回文件的最大数量进程可以打开的,比一个文件描述符的最大可能值多一个。
返回值当前限制在每个进程打开的文件数。

10、getpid/getppid系统调用

功能描述:
getpid返回当前进程标识,getppid返回父进程标识。

头文件:
#include <sys/types.h>
#include <unistd.h>

用法:

pid_t getpid(void);
pid_t getppid(void);

fork()函数用来创建进程

11、unlink函数

功能:删除⼀个名字(某些情况下删除这个名字所指向的⽂件)
头⽂件:#include<unistd.h>
函数原型:

int unlink(const char* pathname);

功能详解:unlink从⽂件系统中中删除⼀个名字,若这个名字是指向这个⽂件的最后⼀个链接,并且没有进程处于打开这个⽂件的状态,则
删除这个⽂件,释放这个⽂件占⽤的空间。
如果这个名字是指向这个⽂件的最后⼀个链接,但有某个进程处于打开这个⽂件的状态,则暂时不删除这个⽂件,要等到打开这个⽂
件的进程关闭这个⽂件的⽂件描述符后才删除这个⽂件。
如果这个名字指向⼀个符号链接,则删除这个符号链接。
如果这个名字指向⼀个socket、fifo或者⼀个设备,则这个socket、fifo、设备的名字被删除,当时打开这些socke、fifo、设备的进程仍
然可以使⽤它们。
返回值:调⽤成功返回0,不成功返回-1;

12、SIGSEGV信号

SIGSEGV信号是当一个进程执行了一个无效的内存引用,或发生段错误时发送给它的信号。

13、setsid()

当进程是会话的领头进程时setsid()调用失败并返回(-1)。setsid()调用成功后,返回新的会话的ID,调用setsid函数的进程成为新的会话的领头进程,并与其父进程的会话组和进程组脱离。由于会话对控制终端的独占性,进程同时与控制终端脱离。setsid后子进程不受终端影响,终端退出,不影响子进程。
参考链接:https://blog.csdn.net/sweetfather/article/details/79457261

14、认识了解uint8_t / uint16_t / uint32_t /uint64_t

https://blog.csdn.net/liweigao01/article/details/84032933

15、%取余注意事项

在对float类型变量进行%取余时,编译出现以下错误:
invalid operands to binary % (have ‘double’ and ‘int’)
原因:double类型不能使用%操作符作取余运算。浮点数没有%运算符,%只能运用于整数。

下面代码,演示了如何使用一个u_int16_t变量的前8位和后8位:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    char *str = "12256"; 

    u_int8_t *byte = NULL;
    u_int16_t temper = 0;

    byte = (u_int8_t*)&temper;
    byte[0] = atoi(str)/1000;
    byte[1] = (atoi(str)%1000)/10;/*因为一个u_int8_t的类型只能存0~255的数,所以只能取两位数以内进行使用*/

    printf("atoi(str):%d, integer:%d, decimal:%d\n", atoi(str), byte[0], byte[1]);

}

16、C文件开头格式

我们在看别人写的规范代码一般开头都有一个固定的程序开头,下面我们看一下STM32给我们安排的顺序,就可以大概知道到底是typedef、全局变量、函数声明、头文件的排放顺序了。
在这里插入图片描述

17、Linux系统中日志文件主要存放目录

日志文件的默认路径是:

/var/log:这个是登录文件放置日志的的目录。/var/log下面是几个重要的日志文件的路径及其包含的信息: /var/log/syslog:它和/etc/log/messages日志文件不同,它只记录警告信息,常常是系统出问题的信息。 /var/log/messages:包括整体系统信息。

大部分Linux发行版默认的日志守护进程为 syslog,位于 /etc/syslog 或 /etc/syslogd,默认配置文件为 /etc/syslog.conf,任何希望生成日志的程序都可以向 syslog 发送信息。

18、%d和%i的区别

C语言中 %d 与 %i 的区别 和注意事项
在 printf 格式串中使用时,没有区别
在 scanf 格式串中使用时,有点区别,如下:
在scanf格式中,%d 只与十进制形式的整数相匹配。
而%i 则可以匹配八进制、十进制、十六进制表示的整数。·
例如: 如果输入的数字有前缀 0(018、025),%i将会把它当作八进制数来处理,如果有前缀0x (0x54),它将以十六进制来处理。

19、>> 和 >>=的区别

m >> 1; // 是指把 m这个数右移一位,得到结果,当时不改变 m本身。
m >>= 1; // 是指把 m这个数右移一位,得到结果,并把结果赋值给 m 因此 m 的值被改变

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值