字符串与内存函数入门到实战指南

一、基础篇:每个程序员必须掌握的8大函数

1.1 strlen - 字符串长度测量仪

基础用法

c

size_t strlen(const char *str);
// 示例:
char name[] = "Alice";
int length = strlen(name); // 返回5

模拟实现:(三种)

c

//strlen的模拟实现(三种)
//递归
//计数器
//指针-指针之间的元素数
int my_strlen(const char*s)//递归
{
	assert(s);
	if (*s == '\0')
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen(++s);
	}
}
int my_strlen(const char*s)//计数器实现
{
 assert(s)
	int count = 0;
	while (*s != '\0')
	{
		count++;
		s++;
	}
	return count;
}
int my_strlen(const char* s)//指针-指针
{
	assert(s);
	char* start = s;
		while (*s != '\0')
	{
		s++;
	}
		return (s - start);
}
int main()
{
	char string[] = "Hello,world!";
	int ret = my_strlen(string);
	printf("The num of the string is %d", ret);
	return 0;
}

超市扫码枪原理
就像扫码枪读取商品条形码时自动计算位数:

c

void process_barcode(const char* code) {
    size_t code_len = strlen(code);
    if(code_len != 13 && code_len != 8) {
        trigger_error("无效条码");
    }
    // 处理有效条码...
}

1.2 strcpy/strncpy - 字符串复印机

基础用法

c

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

// 示例:
char src[] = "Hello";
char dest[10];
strncpy(dest, src, sizeof(dest)-1); // 安全复制
dest[sizeof(dest)-1] = '\0';        // 确保终止符

模拟实现strcpy:

c

//strcpy的实现(n)
//char* strcpy(char * destination, const char * source );
//思路:一个字符串,一个可改并且空间够大的目标空间,会将'\0'拷贝
//两个指针++的形式
char* my_strcpy(char*dest,const char*src)
{
	assert(dest);
	assert(src);
	char* start = dest;
	//while (*src != '\0')//初级
	//{
	//	*dest = *src;
	//	dest ++;
	//	src ++;
	//}
	//*dest = '\0';
	while (*dest++ = *src++)//高级,先执行,再判断。\0就先放进去后判断为0才退出
	{
		;
	}
	return start;
}
int main()
{
	char src[] = "Hello,world";
	char dest[20];
	//strcpy(dest, src);
	char* ret = my_strcpy(dest, src);
	printf("%s", ret);
	return 0;
}

模拟实现strncpy:

c

//模拟实现strncpy
//char * strncpy ( char * destination, const char * source, size_t num )
// 思路:分两种情况来判断,第一种:拷贝的字符数小于原空间字符数
//第二种:拷贝的字符数大于原空间字符数
char* my_strncpy(char* dest, const char* str1, size_t n)
{
	assert(dest);
	assert(str1);
	char* ret = dest;
	size_t len = strlen(str1);
	if (len >= n)
	{
		for (size_t c = 0; c < n; c++)
		{
			*(dest + c) = *(str1 + c);
		}
		dest[n] = '\0';
		return ret;
	}
	else
	{
		for (size_t i = 0; i < len; i++)
		{
			*(dest + i) = *(str1 + i);
		}
		for (size_t j = len; j <n; j++)
		{
			*(dest + j) = '\0';
		}
		return ret;
	}
}
int main()
{
	char dest[100] = "";
	char str1[] = "Hello,world";
	printf("%s", my_strncpy(dest, str1, 3));
	//dest[2] = '\0';
	return 0;
}

快递面单打印系统
防止地址信息过长导致打印错位:

c

void print_address_label(const char* address) {
    char buffer[50];
    strncpy(buffer, address, sizeof(buffer)-1);
    buffer[sizeof(buffer)-1] = '\0';
    
    thermal_printer_print(buffer);
}

1.3 strcat/strncat - 字符串拼接工

基础用法

c

char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);

// 示例:
char path[100] = "/home/";
strncat(path, username, sizeof(path)-strlen(path)-1);

模拟实现strcat:

c

//模拟实现strcat
//char *strcat(char *dest, const char *src);
//思路:现在目标空间找到\0,然后从\0处往后复制,
// 一直到src不为\0处停下,最后再补一个\0作为字符串的终止符
char* my_strcat(char*dest,const char*src)
{
    assert(dest);
    assert(src);
    char* ret = dest;
 /*   while (*dest)
    {
        dest++;
    }*/
    dest = dest + strlen(dest);//通过指针运算找到\0的指针
    while (*src)
    {
        *dest = *src;
        dest++;
        src++;
    }
    *dest = '\0';
        return ret;
}
int main()
{
    char dest[20] = "Hello ";
    char src[] = "world!";
    //char* ret = strcat(dest, src);
    char* ret = my_strcat(dest, src);
    printf("%s",ret);
    return 0;
}

模拟实现strncat:

c

//模拟实现strncat
//char * strncat ( char * destination, const char * source, size_t num );
//思路:找到目标空间的'\0',往后拷贝,分两种情况
//第一:if追加的字符数小于原来的字符数,后面补'\0'
//第二:大于,也补"\0'
char* my_strncat(char* dest, const char* src, size_t num)
{
	assert(dest);
	assert(src);
	char* ret = dest;
	dest = dest + strlen(dest);
	if (num < strlen(src))
	{
		for (size_t i = 0; i < num; i++)
		{
			*(dest + i) = *(src + i);
		}
		dest[num] = '\0';
	}
	else
	{
		for (size_t i = 0; i < strlen(src); i++)
		{
			*(dest + i) = *(src + i);
		}
		dest[strlen(src)] = '\0';
	}
	return ret;
}
int main()
{
	char dest[100] = "Hello,";
	char src[] = "world";
	//char* ret = strncat(dest, src, 2);
	char* ret = my_strncat(dest, src, 10);
	printf("%s", ret);
	return 0;
}

银行账户系统
安全生成账户流水号:

c

void generate_account_id(char* buffer, size_t size) {
    const char* prefix = "BOC-";
    time_t now = time(NULL);
    
    strncpy(buffer, prefix, size-1);
    buffer[size-1] = '\0';
    
    char timestamp[20];
    snprintf(timestamp, sizeof(timestamp), "%ld", now);
    strncat(buffer, timestamp, size-strlen(buffer)-1);
}

1.4 strcmp/strncmp - 字符串裁判员

基础用法

c

int strcmp(const char *str1, const char *str2);
int strncmp(const char *str1, const char *str2, size_t n);

// 示例:
if(strcmp(input, "quit") == 0) exit(0);

模拟实现strcmp:

c

//模拟实现strcmp
//思路:取这两个字符串的指针,遍历比较,比ASCLL的值,相减
int my_strcmp(const char* str1, const char* str2)
{
	assert(str1);
	assert(str2);
	while (*str1 == *str2)//相等为一类,相等的时候判断是否为0,不为0,继续++比较
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
	}
	return *str1 - *str2;//分类讨论,正负分为一类
}
int main()
{
	char str1[100];
	char str2[100];
	printf("请输入两个字符串\n");
	printf("第一个字符串为:");
	scanf("%99s", str1);
	printf("\n第二个字符串为:");
	scanf("%99s", str2);
	printf("\n比较两个字符串的大小\n");
	if (my_strcmp(str1, str2) > 0)
	{
		printf("%s大,%s小", str1,str2);
	}
	else if (my_strcmp(str1, str2) == 0)
	{
		printf("%s与%s一样大", str1, str2);
	}
	else
	{
		printf("%s小,%s大", str1,str2);
	}
	return 0;
}

智能门禁系统
比对门禁卡ID与数据库记录:

c

void check_access_card(const char* card_id) {
    char valid_ids[][16] = {"0123ABCD", "4567EFGH"};
    
    for(int i=0; i<2; i++){
        if(strncmp(card_id, valid_ids[i], 8) == 0) {
            unlock_door();
            return;
        }
    }
    trigger_alarm();
}

1.5 memset - 内存橡皮擦

基础用法

c

void *memset(void *str, int c, size_t n);

// 示例:
int arr[10];
memset(arr, 0, sizeof(arr)); // 数组清零

医院叫号系统
每天营业结束重置系统状态:

c

void daily_reset(SystemStatus* status) {
    memset(status->waiting_queue, 0, sizeof(status->waiting_queue));
    memset(status->current_number, 0, sizeof(status->current_number));
    status->total_patients = 0;
}

1.6 memcpy - 内存搬运工

基础用法

c

void *memcpy(void *dest, const void *src, size_t n);

// 示例:
int src[5] = {1,2,3,4,5};
int dest[5];
memcpy(dest, src, sizeof(src));

模拟实现:

c

//模拟实现memcpy
//void * memcpy ( void * destination, const void * source, size_t num );
//思路:与strcpy的实现类似,但是他是可以接受任意类型的指针的,要强转为char*
//一个个字节来复制,返回起始地址
void* my_memcpy(void* dest, const void* s, size_t num)
{
	assert(dest);
	assert(s);
	void* ret = dest;
	while (num--)
	{
		*(char*)dest = *(char*)s;
		dest = (char*)dest + 1;
		s = (char*)s + 1;
	}
	return ret;
}

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	//memcpy(arr2, arr1, 20);//这里的数字是占多少个字节
	my_memcpy(arr2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

交通信号灯控制系统
备份当前路口信号状态:

c

void backup_traffic_status(TrafficLight* lights, int count) {
    static TrafficLight backup[10];
    memcpy(backup, lights, count * sizeof(TrafficLight));
    // 当主系统故障时恢复备份
}

1.7 memmove - 智能搬运工

基础用法

c

void *memmove(void *dest, const void *src, size_t n);

// 示例:
char str[] = "123456789";
memmove(str+2, str, 5); // 结果为"121234589"

模拟实现:

c

//模拟实现memmove
//void * memmove ( void * destination, const void * source, size_t num );
//分前后两种情况,1.从前向后拷贝,2.从后向前拷贝
void* my_memmove(void* dest, const void* s, size_t num)
{
	assert(dest && s);
	void* ret = dest;
	if (dest < s)
	{
		//从前向后
		while (num--)
		{
			*(char*)dest = *(char*)s;
			dest = (char*)dest + 1;
			s = (char*)s + 1;
		}
	}
	else
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)s + num);
		}
	}
	return ret;
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1+5, arr1, 20);
	int i = 0;
		for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

地铁时刻表调整
动态调整列车运行区间数据:

c

void adjust_schedule(TrainSchedule* schedule, int offset) {
    memmove(schedule->times + offset,
           schedule->times,
           (MAX_STATIONS - offset) * sizeof(Time));
    // 更新调整后的时刻表
}

1.8 strstr - 字符串探测器

基础用法

c

char *strstr(const char *haystack, const char *needle);

// 示例:
char log[] = "Error:404 Not Found";
if(strstr(log, "404")) send_alert();

模拟实现strstr:

c

模拟实现strstr
char * strstr ( const char * str1, const char * str2);
思路:创三个指针变量来记录地址,cur先指向str1,将cur赋给s1,
s2赋给str2,判断相等,不相等,cur++,相等的话,s1与s2各自往下走
如果不相等,就跳出来,cur此时的位置找不到,更新到下一个位置,
s1与s2重新回到原来的地方,继续往下找,直到找到为止,前提是不能为\0
当s2为\0时,说明在寻找的指针中包含了s2所有的东西,返回cur当前所指向的地方
特殊情况:str2为空字符时,就返回str1的地址
如果s1取到\0,那就跳到cur++,重复多几次最终让cur变为\0就可以NULL结束
char* my_strstr(const char* str1, const char* str2)
{
	
	const char* s1 = NULL;
	const char* s2 = NULL;
	const char* cur = str1;
	if (*str2 == '\0')
	{
		return str1;
	}
	while (*cur)
	{
		s1 = cur;
		s2 = str2;
		while (*s1 == *s2 && *s1 != '\0' && *s2 != '\0')
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return cur;
		}
		cur++;
	}
	return NULL;
}
int main()
{
	char string1[] = "Hello,world";
	char string2[] = "ell";
	char* ret = my_strstr(string1, string2);
	if (ret != NULL)
	{
		printf("%s\n", ret);
	}
	else
	{
		printf("找不到");
	}
	return 0;
}

论文查重系统
快速定位重复段落:

c

void check_plagiarism(const char* paper) {
    const char* samples[] = {"量子计算", "区块链", "机器学习"};
    
    for(int i=0; i<3; i++){
        if(strstr(paper, samples[i])) {
            highlight_match(samples[i]);
        }
    }
}

二、实战篇:真实项目中的综合应用

2.1 用户注册系统

c

#define USERNAME_MAX 20
#define PASSWORD_MAX 32

typedef struct {
    char username[USERNAME_MAX];
    char password_hash[PASSWORD_MAX];
} User;

int register_user(const char* username, const char* password) {
    // 检查用户名长度
    if(strlen(username) >= USERNAME_MAX) {
        return -1; // 用户名过长
    }
    
    // 安全拷贝用户名
    strncpy(new_user.username, username, USERNAME_MAX-1);
    new_user.username[USERNAME_MAX-1] = '\0';
    
    // 密码哈希处理
    sha256(password, new_user.password_hash);
    
    // 写入数据库
    return save_to_database(&new_user);
}

2.2 物联网设备通信

c

#pragma pack(push, 1)
struct SensorPacket {
    uint32_t timestamp;
    float temperature;
    float humidity;
    uint16_t checksum;
};
#pragma pack(pop)

void process_sensor_data(uint8_t* raw_data) {
    struct SensorPacket packet;
    
    // 拷贝原始数据
    memcpy(&packet, raw_data, sizeof(packet));
    
    // 校验数据完整性
    if(calculate_crc(raw_data, sizeof(packet)-2) != packet.checksum) {
        log_error("传感器数据校验失败");
        return;
    }
    
    // 温度异常告警
    if(packet.temperature > 85.0f) {
        trigger_temperature_alert();
    }
}

2.3 文件格式解析

c

#define MAX_PATH 260

void read_image_metadata(const char* filename) {
    FILE* fp = fopen(filename, "rb");
    if(!fp) return;
    
    // 读取文件头
    char header[4];
    fread(header, 1, 4, fp);
    
    // 判断文件类型
    if(memcmp(header, "\x89PNG", 4) == 0) {
        process_png(fp);
    } else if(memcmp(header, "GIF8", 4) == 0) {
        process_gif(fp);
    } else {
        printf("未知文件格式");
    }
    
    fclose(fp);
}

三、避坑指南:新手常见错误

3.1 忘记字符串终止符

错误代码

c

char greeting[5];
strncpy(greeting, "Hello", 5); // 没有空间放'\0'
printf("%s", greeting); // 可能打印乱码

正确写法

c

strncpy(greeting, "Hello", sizeof(greeting)-1);
greeting[sizeof(greeting)-1] = '\0';

3.2 混淆strcmp返回值

错误理解

c

if(strcmp(a, b)) { // 错误:返回0表示相等
    // 以为a和b相等时会执行这里
}

正确用法

c

if(strcmp(a, b) == 0) { // 相等时进入分支
    // ...
}

3.3 错误的内存操作大小

危险代码

c

int src[5] = {1,2,3,4,5};
int dest[5];
memcpy(dest, src, 5); // 错误:应该用sizeof(int)*5

正确写法

c

memcpy(dest, src, sizeof(src));

四、知识检测与练习

4.1 选择题

  1. 处理用户输入的用户名时,应该使用哪个函数最安全?(最佳选择:B. strncpy)
    A. strcpy
    B. strncpy
    C. memcpy
    D. strcat

  2. 需要比较两个二进制文件内容是否完全相同时,应该使用:(最佳选择B. memcmp
    A. strcmp
    B. memcmp
    C. strncmp
    D. strstr


4.2 编程练习

实现一个安全版本的字符串拼接函数:

c

void safe_strcat(char* dest, size_t dest_size, const char* src) {
    // 你的代码...
}

// 示例调用:
char buffer[10] = "Hello";
safe_strcat(buffer, sizeof(buffer), "World");
// buffer应为"HelloWor"

通过这种从基础到实战的学习路径,您将能够:

  1. 正确使用每个字符串和内存函数

  2. 避免常见的内存错误

  3. 编写安全可靠的业务代码

  4. 快速定位和修复相关BUG

记住:每个函数都是工具,就像木匠的锤子和锯子,只有正确使用才能造出精美的作品。动手实践是最好的学习方式,现在就开始您的编程之旅吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值