C语言注意事项

发布于:2025-07-31 ⋅ 阅读:(21) ⋅ 点赞:(0)

目录

一、字符串

1、初始化方法

2、字符串、字符数组和整型数组在存储上的区别

3、sizeof和strlen的区别(重点)


一、字符串

1、初始化方法

//字符数组、字符串变量
char str1[] = {'h', 'e', 'l', 'l', 'o'};//方式1:逐个字符初始化
char str2[] = "hello";//方式2:直接赋值
char str3[128] = {'\0'};//方式3:部分赋初值
 
//字符串常量
char *pstr = "hello";//方式4

一般使用 方式2和方式4的初始化方式 

上面两种方法:

第4种使用指针,是字符串常量,不允许数据被修改

如果字符串后面不会修改,则使用char *pstr,如果后面需要操作字符串,就要使用char str2[]或者char str3[128] ,另外的情况就是加长字符串的情况就尽量使用char str3[128]

#include "stdio.h"

int main(int argc, char *argv[])
{
    char *pstr = "12345";
    printf("pstr:%s\n",pstr);
//    pstr[0] = 'w';   //会报错误,无法运行
//    printf("pstr:%s",pstr);
    char str1[] = "abcdef";
    printf("str1:%s\n",str1);
    str1[0] = 'w';
    printf("str1:%s\n",str1);

}

2、字符串、字符数组和整型数组在存储上的区别

#include <stdio.h>
 
int main()
{
    int length;
 
    int data[] = {1,2,3,4,5};//整型数组
    char cdata[] = {'h','e','l','l','o'}; //表示是一个字符数组
    char cdata2[] = "hello"; //表示是一个字符串,编译器会自动加上'\0'
    char cdata3[] = {'h','e','l','l','o','\0'};//字符串的结束标志 '\0'
 
    length = sizeof(data) / sizeof(data[0]);
    printf("data的长度:%d\n",length);
 
    length = sizeof(cdata) / sizeof(cdata[0]);
    printf("cdata的长度:%d\n",length);
 
    length = sizeof(cdata2) / sizeof(cdata2[0]);
    printf("cdata2的长度:%d\n",length);
 
    length = sizeof(cdata3) / sizeof(cdata3[0]);
    printf("cdata3的长度:%d\n",length);
 
    return 0;
}

  1. int data[] = {1, 2, 3, 4, 5};
    1. 这是一个整型数组,包含5个元素。

    2. sizeof(data) 返回整个数组占用的字节数。

    3. sizeof(data[0]) 返回单个整型元素占用的字节数。

    4. 数组长度计算公式:sizeof(data) / sizeof(data[0])

  2. char cdata[] = {'h', 'e', 'l', 'l', 'o'};
    1. 这是一个字符数组,包含5个字符。

    2. 它不是字符串,因为没有以'\0'结尾。

    3. sizeof(cdata) 返回整个字符数组占用的字节数 5。

  3. char cdata2[] = "hello";
    1. 这是一个字符串,编译器会自动在末尾添加'\0'

    2. 因此,cdata2的长度是6(5个字符 + 1个'\0')。

  4. char cdata3[] = {'h', 'e', 'l', 'l', 'o', '\0'};
    1. 这是一个显式以'\0'结尾的字符数组,等价于字符串。

    2. 因此,cdata3的长度也是6。

3、sizeof和strlen的区别(重点)

1.sizeof

  • 作用:sizeof 用于计算数据类型或变量占用的内存大小(以字节为单位)。
  • 适用范围:
    • 数据类型(如 int、char 等)。
    • 变量(如数组、结构体等)。
  • 对于字符串:
    • sizeof 返回整个字符数组占用的字节数,包括字符串末尾的 '\0'。
  • 特点:
    • 在编译时计算,不会实际运行代码。
    • 对于数组,返回整个数组占用的字节数。
    • 对于指针,返回指针本身的大小(通常是4或8字节,取决于系统架构)。
#include <stdio.h>
 
int main()
{
    char cdata[] = "hello";
 
    printf("sizeof:%d\n",sizeof(cdata));// 输出 6(5个字符 + 1个'\0')
 
    return 0;
}

2.strlen(定义在 <string.h> 头文件中)

  • 作用:strlen 是一个运行时函数,用于计算字符串的长度(即字符的数量,不包括末尾的 '\0')。
  • 适用范围:
    • 仅适用于以 '\0' 结尾的字符串。
  • 返回值:返回一个 size_t 类型的值,表示字符串的长度。
  • 特点:
    • 在运行时计算,会遍历字符串直到遇到 '\0'。
    • 不计算 '\0' 的长度。
    • 如果字符串没有以 '\0' 结尾,strlen 的行为是未定义的(可能导致内存越界访问)。
#include <stdio.h>
#include <string.h>
 
int main()
{
    char cdata[] = "hello";
 
    printf("strlen:%d\n",strlen(cdata));// 输出 5(不包括'\0')
 
    return 0;
}

3. 主要区别 

4.代码示例

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

int main()
{
    char cdata[] = "hello";

    printf("sizeof:%d\n", sizeof(cdata));
    //sizeof 返回的是整个数组的大小(包括'\0')

    printf("strlen:%d\n",strlen(cdata));
    //strlen返回字符串的长度(不包括'\0')

    return 0;
}

5. 常见误区 

混淆 sizeof 和 strlen

  • sizeof 计算的是内存大小,strlen 计算的是字符串的有效长度。

char str[100] = "hello";
printf("%d\n", sizeof(str)); // 输出 100
printf("%d\n", strlen(str)); // 输出 5

未以 '\0' 结尾的字符数组

  • 如果字符数组没有以 '\0' 结尾,strlen 的行为是未定义的。

char str[] = {'h', 'e', 'l', 'l', 'o'}; // 不是字符串
printf("%d\n", strlen(str)); // 未定义行为

指针 vs 数组

  • 对于指针,sizeof 返回指针本身的大小,而不是字符串的大小。

char *str = "hello";
printf("%d\n", sizeof(str)); // 输出 8(str是一个指针“地址”,在系统中通常以8个字节表示一个地址)
printf("%d\n", strlen(str)); // 输出 5

总结

  • sizeof:用于计算变量或类型占用的内存大小,适用于所有数据类型,包括字符串。

  • strlen:用于计算字符串的长度,仅适用于以 '\0' 结尾的字符串。

4、动态创建字符串

使用malloc动态创建内存,然后用free回收内存,memset初始化字符串

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main()
{
    char *p;
 
    p = (char *)malloc(1);
    if( p == NULL )
    {
        printf("malloc error\n");
        exit(-1);
    }
    memset(p,'\0',1);
    *p = 'a';
    printf("%c\n",*p);
 
    free(p);//释放内存
    p = NULL;//释放内存后,如p不指向NULL,会变成悬挂指针
 
    p = (char *)malloc(6);
    if( p == NULL )
    {
        printf("malloc error\n");
        exit(-1);
    }
    memset(p,'\0',6);
    strcpy(p,"hello");    
    printf("%s\n",p);
 
    return 0;
}

使用 realloc 扩容内存

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main()
{
    char *p;
 
    p = (char *)malloc(1 * sizeof(char)); //动态开辟内存给p
    // 检查是否开辟内存成功
    if( p == NULL )
    {
        printf("malloc error\n");
        exit(-1);
    }
 
    int length = strlen("hello world 666666");
    int newLength = 1 + length + 1;
 
    char *p2 = realloc(p,newLength); //创建一个新的指针变量,指向realloc扩展后的内存空间
    // 检查是否拓展内存成功
    if( p2 == NULL )
    {
        free(p);
        p = NULL;
        exit(-2);
    } 
    else
    {
        p = p2;//更新指针指向,使p指向扩展后的内存地址空间
    } 
 
    strcpy(p,"hello world 666666");
    printf("%s\n",p);
    
    return 0;
}

5、字符串常用API

拷贝函数(strcpy 和 strncpy)

  • strcpy
功能

strcpy 用于将一个字符串(包括终止符 '\0')从源地址复制到目标地址。

函数原型
char* strcpy(char *dest, const char *src);

cpp运行

参数
  • dest:目标字符串的起始地址,用于存储复制后的字符串。

  • src:源字符串的起始地址,需要复制的字符串。

返回值
  • 返回目标字符串的起始地址 dest

特点
  1. 复制整个字符串

    • strcpy 会复制 src 中的所有字符,直到遇到 '\0'(包括 '\0')。

    • 例如,如果 src 是 "hello"strcpy 会复制 'h''e''l''l''o''\0' 到 dest

  2. 不检查目标缓冲区大小

    • strcpy 不会检查 dest 是否有足够的空间来存储 src 的内容。

    • 如果 src 的长度大于 dest 的容量,会导致 缓冲区溢出,这是一种严重的安全漏洞。

  3. 适合已知安全的场景

    • 只有在确保 src 的长度不超过 dest 的容量时,才适合使用 strcpy

#include <stdio.h>
#include <string.h>
 
int main() {
    char dest[20];
    char *src = "Hello, World!";
 
    strcpy(dest, src); // 复制 src 到 dest
    printf("dest: %s\n", dest); // 输出: dest: Hello, World!
 
    return 0;
}

手写 strcpy 函数

#include <stdio.h>
 
char* myStrcpy(char *dest, const char *src)
{
    //判断 dest 和 src 是否 == NULL,如果是return NULL,防止解引用空指针
    if( dest == NULL || src == NULL )
    {
        return NULL;
    }
 
    char *bak = dest; //备份地址头
 
    //复制字符
    while ( *src != '\0' )
    {
        *dest = *src;
        dest++;
        src++;
    }
 
    *dest = '\0';//手动添加结束标志:'\0'
 
    return bak;
}
 
int main()
{
    char str[128] = {'\0'};
    char *pstr = "xiaolin handsome";
 
    myStrcpy(str, pstr);
 
    printf("%s\n",str);
 
    return 0;
}
  • strncpy
功能

strncpy 用于从源地址复制最多 n 个字符到目标地址。如果 src 的长度小于 n,则用 '\0' 填充剩余部分。

函数原型
char* strncpy(char *dest, const char *src, size_t n);

cpp运行

参数
  • dest:目标字符串的起始地址,用于存储复制后的字符串。

  • src:源字符串的起始地址,需要复制的字符串。

  • n:最多复制的字符数(包括 '\0')。

返回值
  • 返回目标字符串的起始地址 dest

特点
  1. 限制复制的字符数

    • strncpy 会复制最多 n 个字符。

    • 如果 src 的长度小于 n,则复制 src 的所有字符,并用 '\0' 填充剩余部分。

    • 如果 src 的长度大于或等于 n,则只复制前 n 个字符,不会自动添加 '\0'

  2. 避免缓冲区溢出

    • 通过限制复制的字符数,strncpy 可以避免目标缓冲区溢出的问题。

    • 但需要注意,如果 n 大于 dest 的容量,仍然可能导致溢出。

  3. 适合需要控制复制长度的场景

    • 当需要限制复制的字符数时,strncpy 是一个更安全的选择。

#include <stdio.h>
#include <string.h>
 
int main() {
    char dest[20];
    char *src = "Hello, World!";
 
    strncpy(dest, src, 5); // 复制 src 的前 5 个字符到 dest
    dest[5] = '\0'; // 手动添加终止符
    printf("dest: %s\n", dest); // 输出: dest: Hello
 
    return 0;
}

手写strncpy函数

#include <stdio.h>
 
char* myStrncpy(char *dest, const char *src, int num)
{
    //判断 dest 和 src 是否 == NULL,如果是return NULL,防止解引用空指针
    if( dest == NULL || src == NULL )
    {
        return NULL;
    }
 
    char *bak = dest;
    int numChar = 1; //numChar是一个计数器,用于记录已经复制的字符数量,从1开始记录。
 
    // 最多复制num个字符
    while ( *src != '\0' && numChar <= num )
    {
        *dest = *src;
        dest++;
        src++;
        numChar++;
    }
 
 
    //这个判断的作用是检查是否已经复制了足够的字符(即是否已经复制了num个字符)。
    if( numChar <= num )
    {
        // src的长度小于num,则在dest中填充'\0',直到达到num个字符
        while ( numChar <= num ) 
        {
            *dest = '\0';
            dest++;
            numChar++;
        }
        return bak;
    }
 
    *dest = '\0';//手动添加字符串结束标志:'\0'
 
    return bak;
}
int main()
{
    char str[128] = {'\0'};
    char *pstr = "xiaolin handsome";
 
    myStrncpy(str, pstr, 7);
    printf("%s\n",str);
 
    return 0;
}

拼接函数(strcat 和 strncat

strcat 是 字符串拼接函数,用于将一个字符串(源字符串)追加到另一个字符串(目标字符串)的末尾。

strcat
1. 函数原型
#include <string.h>
char *strcat(char *dest, const char *src);
  • 功能:将 src 字符串(包括结尾的 '\0')追加到 dest 字符串的末尾。
  • 返回值:返回目标字符串的指针 dest。
2. 关键要求
  • dest 必须指向一个足够大的缓冲区,能够容纳 dest 和 src 的总长度(包括末尾的 '\0')。
  • dest 和 src 必须是以 '\0' 结尾的字符串。
#include <stdio.h>
#include <string.h>
 
int main() {
    char dest[20] = "Hello, ";
    const char *src = "World!";
    
    strcat(dest, src);  // 将 src 追加到 dest 末尾
    printf("%s\n", dest);  // 输出:Hello, World!
    
    return 0;
}

手写 strcat 函数

#include <stdio.h>
#include <assert.h>
 
char* myStrcat(char *dest, const char *src)
{
    //使用 assert 检查 dest 和 src 是否为非空指针;如为空指针,退出程序并提示开发者
    assert( dest != NULL && src != NULL );
 
	char *bak = dest;
 
	//通过 while(*dest != '\0') 找到目标字符串 dest 的末尾(即 \0 的位置)。
	while( *dest != '\0' )
	{
		dest++;//地址偏移
	}
 
    //将 src 的字符逐个复制到 dest 末尾,直到 src 遇到 \0。
	while( *src != '\0' )
	{
		*dest = *src;
		dest++;
		src++;
	}
 
    //手动在拼接后的字符串末尾添加 \0。
	*dest = '\0';
 
	return bak;
}
 
int main()
{
	char str[128] = "xiaolin";
	char *pstr = " handsome";
 
	myStrcat(str, pstr);
	printf("%s\n",str);
 
	return 0;
}
strncat

strncat用于将源字符串的指定数量字符追加到目标字符串末尾,避免缓冲区溢出。

1. 函数原型
#include <string.h>
char *strncat(char *dest, const char *src, size_t n);
  •     功能:从 src 字符串中复制最多 n 个字符到 dest 末尾(自动添加 \0)。
  •     返回值:返回目标字符串的指针 dest。
2. 核心特性
安全机制
  •     限制最大复制长度:通过 n 参数控制追加字符数量,防止目标缓冲区溢出。
  •     自动添加终止符:无论是否复制完 n 个字符,strncat 都会在 dest 末尾添加 \0。
#include <stdio.h>
#include <string.h>
 
int main() {
    char dest[20] = "Hello";
    const char *src = ", World!";
    
    // 安全追加最多 7 个字符
    strncat(dest, src, 7);  
    printf("%s\n", dest);  // 输出:Hello, World
    
    return 0;
}

手写 strncat 函数

#include <stdio.h>
#include <string.h>
#include <assert.h>
 
char* myStrncat(char *dest, const char *src, int num_limit, int array_total)
{
    assert( dest != NULL && src != NULL );
 
    char *bak = dest;
 
    // 计算剩余可用空间大小:数组总大小 - 数组有效字节 - 1('\0')== 剩余可用空间
    int remaining = array_total - strlen(dest) - 1;
 
    // 移动到 dest 的末尾
    while ( *dest != '\0')
    {
        dest++;
    }
 
    //如果剩余字节大小 大于或者等于 需要复制的最大字节数
    if( remaining >= num_limit )
    {
        //复制最大的字节数
        while( *src != '\0' && num_limit > 0 )
        {
            *dest = *src;
            dest++;
            src++;
            num_limit--;
        }
        *dest = '\0';
    }
    //如果剩余字节大小 小于 需要复制的最大字节数
    else if( remaining < num_limit )
    {
        //复制剩余字节大小
        while( *src != '\0' && remaining > 0 )
        {
            *dest = *src;
            dest++;
            src++;
            remaining--;
        }
        *dest = '\0';
    }
 
    return bak;
}
 
int main()
{
    char str[14] = "hello"; // 6个字节
    char *pstr = " xiaoLin"; // 9个字节(8个有效字节 + 1个'\0')
 
    myStrncat(str, pstr, 8, sizeof(str));
    printf("%s\n",str);
 
    return 0;
}

字符串比较函数(strcmp 和 strncmp)

strcmp

strcmp 是 C 语言中用于比较两个字符串(以 \0 结尾的字符数组)的标准库函数。它定义在 <string.h> 头文件中。
函数原型:

int strcmp(const char *str1, const char *str2);
  • 参数:
    • str1:要比较的第一个字符串(以 \0 结尾)。
    • str2:要比较的第二个字符串(以 \0 结尾)。
  • 返回值:
    • 0:如果两个字符串相等(内容完全相同)。
    • 负整数:如果 str1 小于 str2(按字典序比较)。
    • 正整数:如果 str1 大于 str2(按字典序比较)。

工作原理:

strcmp 会逐个字符比较两个字符串,直到遇到不同的字符或到达字符串的结束符 \0。比较是基于字符的 ASCII 值。

#include <stdio.h>
#include <string.h>
 
int main() {
    char str1[] = "apple";
    char str2[] = "banana";
    char str3[] = "apple";
 
    // 比较 str1 和 str2
    int result1 = strcmp(str1, str2);
    if (result1 == 0) {
        printf("str1 和 str2 相等\n");
    } else if (result1 < 0) {
        printf("str1 小于 str2\n"); // 输出:str1 小于 str2
    } else {
        printf("str1 大于 str2\n");
    }
 
    // 比较 str1 和 str3
    int result2 = strcmp(str1, str3);
    if (result2 == 0) {
        printf("str1 和 str3 相等\n"); // 输出:str1 和 str3 相等
    } else {
        printf("str1 和 str3 不相等\n");
    }
 
    return 0;
}

手写 strcmp 函数

#include <stdio.h>
#include <assert.h>
 
int myStrcmp(const char *str1, const char *str2)
{
    assert(str1 != NULL && str2 != NULL);
 
    // 逐个字符比较
    while (*str1 != '\0' && *str2 != '\0' && *str1 == *str2) {
        str1++;
        str2++;
    }
 
    // 返回字符差值
    return (*str1 - *str2);
}
 
int main()
{
    char *p = "xiaoLin hello";
    char *p2 = "handsome";
 
    int ret = myStrcmp(p, p2);
 
    printf("myStrcmp return = %d\n", ret);
 
    return 0;
}
strncmp

strncmp 是 C 语言标准库中的一个字符串比较函数,用于比较两个字符串的前 n 个字符。它的声明在 <string.h> 头文件中。 

函数原型
int strncmp(const char *str1, const char *str2, size_t n);
  • 参数
    • str1:第一个要比较的字符串。
    • str2:第二个要比较的字符串。
    • n:要比较的最大字符数。
  • 返回值
    • 如果 str1 和 str2 的前 n 个字符相等,返回 0。
    • 如果 str1 的前 n 个字符小于 str2 的前 n 个字符,返回一个负整数。
    • 如果 str1 的前 n 个字符大于 str2 的前 n 个字符,返回一个正整数。
  • 注意事项
    • strncmp 只比较前 n 个字符,即使字符串长度超过 n,也不会比较后面的字符。
    • 如果 n 大于两个字符串的长度,strncmp 会在遇到字符串的结束符 \0 时停止比较。
  • 与 strcmp 的区别
    • strcmp 比较整个字符串,直到遇到 \0 结束符。
    • strncmp 只比较前 n 个字符,即使字符串长度超过 n。
  • 适用场景
    • 当你只需要比较字符串的前几个字符时,可以使用 strncmp。
    • 例如,比较文件扩展名、协议头等场景。
#include <stdio.h>
#include <string.h>
 
int main() {
    char str1[] = "Hello, World!";
    char str2[] = "Hello, C!";
    int result;
 
    // 比较前 5 个字符
    result = strncmp(str1, str2, 5);
 
    if (result == 0) {
        printf("前 5 个字符相等\n");
    } else if (result < 0) {
        printf("str1 的前 5 个字符小于 str2 的前 5 个字符\n");
    } else {
        printf("str1 的前 5 个字符大于 str2 的前 5 个字符\n");
    }
 
    return 0;
}

查找子字符(strchr)

strchr 是 C 语言标准库中的一个字符串查找函数,用于在字符串中查找指定字符的第一次出现位置。它的声明在 <string.h> 头文件中。

函数原型
char *strchr(const char *str, int c);
  • 参数
    • str:要搜索的字符串。
    • c:要查找的字符(以 int 形式传递,但实际会被转换为 char)。
  • 返回值
    • 如果找到字符 c,返回指向该字符的指针。
    • 如果未找到字符 c,返回 NULL。
  • 注意事项
    • 查找字符的范围:
    • strchr 会从字符串的开头开始查找,直到遇到字符串的结束符 \0。
    • 如果字符 c 是 \0,strchr 也会返回指向 \0 的指针。
  • 区分大小写:
    • strchr 是区分大小写的。例如,查找 'w' 和 'W' 是不同的。
  • 返回值:
    • 返回的指针指向的是字符串中第一次出现字符 c 的位置。
    • 如果未找到字符,返回 NULL。
#include <stdio.h>
#include <string.h>
 
int main() {
    char str[] = "Hello, World!";
    char *result;
 
    // 查找字符 'W'
    result = strchr(str, 'W');
 
    if (result != NULL) {
        printf("找到字符 'W',位置在:%ld\n", result - str);
        printf("从该字符开始的子字符串为:%s\n", result);
    } else {
        printf("未找到字符 'W'\n");
    }
 
    return 0;
}

查找子串(strstr)

strstr 是 C 语言标准库中的一个字符串查找函数,用于在一个字符串中查找另一个子串的第一次出现位置。它的声明在 <string.h> 头文件中。

函数原型
char *strstr(const char *haystack, const char *needle);
  • 参数
    • haystack:被搜索的主字符串。
    • needle:要查找的子串。
  • 返回值
    • 如果找到子串 needle,返回指向主字符串中第一次出现子串的指针。
    • 如果未找到子串 needle,返回 NULL。
  • 注意事项
    • 查找范围:
      • strstr 会从主字符串的开头开始查找,直到遇到主字符串的结束符 \0。
      • 如果子串 needle 是空字符串(""),strstr 会返回指向主字符串开头的指针。
    • 区分大小写:
      • strstr 是区分大小写的。例如,查找 "world" 和 "World" 是不同的。
    • 返回值:
      • 返回的指针指向的是主字符串中第一次出现子串的位置。
      • 如果未找到子串,返回 NULL。
  • 与 strchr 的区别
    • strchr 查找单个字符的第一次出现位置。
    • strstr 查找子串的第一次出现位置。
#include <stdio.h>
#include <string.h>
 
int main() {
    char haystack[] = "Hello, World! This is a test string.";
    char needle[] = "World";
    char *result;
 
    // 查找子串 "World"
    result = strstr(haystack, needle);
 
    if (result != NULL) {
        printf("找到子串 \"%s\",位置在:%ld\n", needle, result - haystack);
        printf("从该子串开始的字符串为:%s\n", result);
    } else {
        printf("未找到子串 \"%s\"\n", needle);
    }
 
    return 0;
}

字符串分割(strtok)

strtok 是 C 语言标准库中的一个字符串分割函数,用于将一个字符串按照指定的分隔符拆分成多个子串(标记)。它的声明在 <string.h> 头文件中。

函数原型
char *strtok(char *str, const char *delim);
  • 参数
    • str:要分割的字符串(不能是常量字符串)。在第一次调用时传入待分割的字符串,后续调用传入 NULL。
    • delim:分隔符字符串。strtok 会将字符串中任何属于 delim 的字符作为分隔符。
  • 返回值
    • 返回指向当前分割出的子串的指针。
    • 如果没有更多的子串可分割,返回 NULL。
  • 注意事项
  1. 修改原字符串:
    1. strtok 函数会修改原始字符串,它通过将分隔符替换为字符串结束符 '\0' 来实现分割。然而,你定义的字符串 p 是一个字符串常量,存储在只读内存区域,对其进行修改会导致未定义行为。因此,如果需要保留原字符串,应该先复制一份。
  2. 线程安全问题:
    1. strtok 是不可重入的,因为它使用静态缓冲区来保存状态。如果在多线程环境中使用,可能会导致问题。
    2. 可以使用 strtok_r(POSIX 标准)作为线程安全的替代品。
  3. 连续分隔符:
    1.         如果字符串中有连续的分隔符(例如 ",,"),strtok 会忽略它们,不会返回空字符串。
  4. 空字符串:
  5. 如果字符串中没有任何分隔符,strtok 会返回整个字符串作为唯一的子串。
#include <stdio.h>
#include <string.h>
 
int main()
{
    // 使用字符数组存储字符串,这样可以被修改
    char p[] = "hello my name is xiaoAi";
    char *p2 = " ";
 
    char *result = NULL;
    
    // 第一次调用strtok函数
    result = strtok(p, p2);
    while (result != NULL)
    {
        //输出第一次分割的字符串,后续会继续输出分割的字符串,直至被全部分割完成
        printf("分割出的子字符串:%s\n", result);
        // 后续调用strtok函数,传入NULL
        result = strtok(NULL, p2);
    }
 
    return 0;
}

代码解释

  1. 使用字符数组存储字符串:char p[] = "hello my name is xiaoAi";,这样 p 是一个字符数组,存储在可写内存区域,strtok 函数可以对其进行修改。
  2. strtok 函数的使用:
    1. 第一次调用 strtok(p, p2) 时,传入要分割的字符串 p 和分隔符字符串 p2,函数会返回第一个分割出的子字符串的指针。
    2. 后续调用 strtok(NULL, p2) 时,传入 NULL 表示继续对之前的字符串进行分割,函数会返回下一个分割出的子字符串的指针。
  3. 循环打印分割结果:使用 while 循环,只要 result 不为 NULL,就打印分割出的子字符串,并继续调用 strtok 函数进行分割。

6、字符串格式化函数sprintf和snprintf

sprintf 是 C 语言标准库 <stdio.h> 中的一个函数,主要用于字符串处理。其核心功能是将格式化的数据写入指定的字符串缓冲区。

在使用 sprintf 时,会借助格式控制字符串中的格式符来规定输出数据的格式。常见的格式符有 %d(用于输出整数)、%f(用于输出浮点数)、%c(用于输出字符)、%s(用于输出字符串)等。

#include <stdio.h>
 
int main()
{
    char buffer[50];
    int num = 123;
    int length = sprintf(buffer, "The number is %d", num);
    printf("The formatted string is: %s\n", buffer);
    printf("The length of the formatted string is: %d\n", length);
    return 0;
}

sprintf 函数会将格式化后的字符串 "The number is 123" 写入 buffer 数组,并且返回实际写入到缓冲区的字符数量(不包括字符串终止符 '\0')。

不过,sprintf 存在一个显著的安全隐患,它不会对写入的字符数量进行限制。要求我们在使用时,必须保证目标缓冲区足够大,能够容纳格式化后的字符串,否则就会引发缓冲区溢出问题,可能导致程序崩溃或产生安全漏洞。

sprintf 和 printf 函数在用法上颇为相似,但二者的输出目标不同。printf 函数将格式化的数据输出到标准输出流(通常是屏幕),而 sprintf 则将数据输出到指定的字符串缓冲区。

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

int main()
{
    char Air724_buf[900];
    char Air724_LenDataBuf[1000];

    int ADC = 1000;
    int j_flag = 0;
    char *devId = "0001";
    strcpy(Air724_buf,"");   //先清空字符串
    j_flag = 0;
    j_flag = sprintf(Air724_buf,"A1A1");  //字符串设置A1A1头
    j_flag += sprintf(Air724_buf + j_flag, devId); //添加设备ID
    for(int i = 0; i < 10; i++)
    {
        ADC++;
        //sprintf的返回值都是写入字符的长度
        j_flag += sprintf(Air724_buf + j_flag, "%04d", ADC);  //将每一次的ADC的值按照4位写入字符串后面,Air724_buf + j_flag就是每次写入的位置
    }
    j_flag += sprintf(Air724_buf + j_flag, "%s","D0D0");//末尾加入结束
    sprintf(Air724_LenDataBuf,"%04d",strlen(Air724_buf)); //前面加入长度
    strcat(Air724_LenDataBuf,Air724_buf);  //后面接上字符串
    printf("传输数据:%s",Air724_LenDataBuf);

    return 0;
}

snprintf 同样是 C 语言标准库 <stdio.h> 中的函数,主要作用也是把格式化的字符串存储到一个字符数组中。与 sprintf 不同的是,snprintf 提供了一个额外的参数,用于限制输出的最大字符数,从而避免因格式化字符串过长而引发的缓冲区溢出问题。

snprintf 的返回值规则与 sprintf 不同。如果格式化后的字符串长度小于指定的缓冲区大小 n,snprintf 会将整个字符串写入缓冲区,并返回实际写入的字符数量(不包括字符串终止符 '\0');如果格式化后的字符串长度大于或等于 n,snprintf 会将字符串截断,只写入 n - 1 个字符,然后在末尾添加 '\0',此时返回值为格式化字符串原本应有的长度(即如果缓冲区足够大时会写入的字符数量,不包括 '\0')。示例代码如下:

#include <stdio.h>
 
int main()
{
    char buffer[10];
    const char *str = "This is a long string";
    int result = snprintf(buffer, sizeof(buffer), "%s", str);
    printf("The actual string in buffer is: %s\n", buffer);
    printf("The return value of snprintf is: %d\n", result);
    return 0;
}

由于 buffer 的大小为 10,而要格式化的字符串 "This is a long string" 长度超过了 9(因为要留一个位置给 '\0'),snprintf 会将字符串截断,只写入前 9 个字符,返回值则是 "This is a long string" 的实际长度(不包括 '\0')。

在实际开发过程中,为了提高程序的安全性和稳定性,建议优先使用 snprintf 来替代 sprintf,以此避免缓冲区溢出带来的风险。

sprintf 与 snprintf 的返回值对比

函数 返回值含义
sprintf 返回实际写入缓冲区的字符数(不包括 \0)。如果缓冲区不足,会导致未定义行为。
snprintf 返回想要写入的字符数(不包括 \0),而不是实际写入的字符数。如果缓冲区不足,字符串会被截断。


网站公告

今日签到

点亮在社区的每一天
去签到