日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

深入了解C語言中的字符串和內存函數_C 語言

作者:努力學習游泳的魚 ? 更新時間: 2022-12-06 編程語言

1. 前言

大家好,我是努力學習游泳的魚。今天我們來學習一些常用的庫函數。有了這些庫函數,我們可以更加方便地操作字符串和內存,從而提升我們的編碼效率。話不多說,我們開始吧!

注:以下大部分函數對應的頭文件都是string.h。

2. 求字符串長度

2.1 strlen

size_t strlen ( const char * str );

strlen函數可以求字符串的長度。使用時只需把字符串的起始位置的地址作為參數傳遞給strlen。該函數會從起始位置一直往后數字符,直到遇到\0。最終返回的是\0之前字符的個數。

參數指向的字符串必須以\0結束。否則求出來的是隨機值。

返回類型是size_t,是無符號類型。

接下來使用三種方式來模擬實現strlen。

// 1. 使用計數器
size_t my_strlen(const char* str)
{
	int count = 0;
	assert(str != NULL);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}

// 2. 遞歸
size_t my_strlen(const char* str)
{
	assert(str != NULL);
	if (*str == '\0')
		return 0;
	else
		return 1 + my_strlen(str + 1);
}

// 3. 指針-指針
size_t my_strlen(const char* str)
{
	assert(str != NULL);
	char* begin = str;
	// 找\0
	while (*str != '\0')
	{
		str++;
	}
	return str - begin;
}

3. 長度不受限制的字符串函數

3.1 strcpy

char* strcpy(char * destination, const char * source );
  • strcpy函數會把源字符串拷貝到目標空間中去。
  • 源字符串必須以\0結束。
  • 會將源字符串中的\0拷貝到目標空間。
  • 目標空間必須足夠大,以確保可以存放源字符串。
  • 目標空間必須可變。
  • strcpy返回的是目標空間的起始地址。

接下來我們來模擬實現strcpy函數。

char* my_strcpy(char* dest, const char* src)
{
    char* ret = dest;
    assert(dest && src);
    while (*dest++ = *src++)
    {
        ;
    }
    return ret;
}

3.2 strcat

char * strcat ( char * destination, const char * source );
  • strcat會把源字符串追加到目標字符串后面。
  • 源字符串必須以\0結束。
  • 會把源字符串的\0拷貝到目標空間中去。
  • 目標空間必須足夠大,以確保可以存放源字符串。
  • 目標空間必須可變。
  • strcat返回的是目標空間的起始地址。
  • 不能自己給自己追加,因為當源字符串和目標空間重合時,會覆蓋掉源字符串后面的\0。

有沒有發現,其中很多點和strcpy很像?

接下來我們來模擬實現strcat。只需要兩步:

  • 找到目標空間的\0。
  • 從目標空間的\0開始,把源字符串拷貝到目標空間中去。
char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;

	// 找目標空間的\0
	while (*dest)
	{
		dest++;
	}
	// 從目標空間的\0開始,向后拷貝
	while (*dest++ = *src++)
	{
		;
	}

	return ret;
}

3.3 strcmp

int strcmp ( const char * str1, const char * str2 );
  • strcmp函數比較的不是字符串的長度,而是比較字符串中對應位置上的字符的大小,如果相同,就比較下一對兒,直到不同或者都遇到\0。
  • 若str1<str2,則返回值為負數;若str1>str2,則返回值為正數;若str1=str2,則返回值為0。

接下來我們來模擬實現strcmp。

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);

	while (*str1 == *str2)
	{
		if (*str1 == '\0')
		{
			return 0; // 相等
		}

		str1++;
		str2++;
	}
	// 不相等
	if (*str1 > *str2)
	{
		return 1;
	}
	else
	{
		return -1;
	}
}

4. 長度受限制的字符串函數

4.1 strncpy

char * strncpy ( char * destination, const char * source, size_t num );
  • 拷貝num個字符從源字符串到目標空間。
  • 如果源字符串的長度小于num,則拷貝完源字符串后,在目標的后面追加0,直到num個。

下面是strncpy的模擬實現。

char* my_strncpy(char* dest, const char* src, size_t count)
{
	assert(dest && src);
	char* start = dest;

	while (count && (*dest++ = *src++) != '\0')
	{
		count--;
	}

	if (count)
	{
		while (--count)
		{
			*dest++ = '\0';
		}
	}

	return start;
}

4.2 strncat

char * strncat ( char * destination, const char * source, size_t num );
  • 在目標空間后最多追加num個字符。
  • 如果num大于源字符串的長度,則num直接看作源字符串的長度。
  • 一定會在最后追加\0。

模擬實現如下:

char* my_strncat(char* front, const char* back, size_t count)
{
	assert(front && back);
	char* start = front;

	// 找front中的\0
	while (*front)
	{
		front++;
	}
	// 拷貝
	while (count--)
	{
		if ((*front++ = *back++) == '\0')
		{
			return start;
		}
	}

	*front = '\0';
	return start;
}

4.3 strncmp

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

只比較前num個字符。

以下是模擬實現:

int my_strncpy(const char* s1, const char* s2, size_t count)
{
	assert(s1 && s2);

	while (1)
	{
		if (count == 0)
		{
			return 0;
		}
		else if (*s1 > *s2)
		{
			return 1;
		}
		else if (*s1 < *s2)
		{
			return -1;
		}
		else
		{
			if (*s1 == '\0')
			{
				return 0;
			}
			s1++;
			s2++;
			count--;
		}
	}
}

5. 字符串查找

5.1 strstr

char * strstr ( const char *str1, const char * str2);

在str1中查找str2,如果找到了,就返回第一次出現的起始位置;如果找不到,就返回空指針NULL。

最簡單的實現方式是直接暴力查找。

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);

	const char* s1 = str1;
	const char* s2 = str2;
	const char* cur = str1;

	while (*cur)
	{
		s1 = cur;
		s2 = str2;

		while (*s1 && *s2 && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			// 找到了
			return (char*)cur;
		}

		cur++;
	}

	// 找不到
	return NULL;
}

5.2 strtok

char * strtok ( char * str, const char * sep );
  • strtok用于分割字符串。
  • sep是一個字符串,定義了用作分隔符的字符集合。
  • 第一個參數指定一個字符串,它包含了0個或者多個由sep字符串中一個或者多個分隔符分割的標記。
  • strtok函數找到str中的下一個標記,并將其用\0結尾,返回一個指向這個標記的指針。(注:strtok函數會改變該字符串,所以在使用strtok函數切分的字符串一般都是臨時拷貝的內容并且可修改。)
  • strtok函數的第一個參數不為NULL,函數將找到str中第一個標記,strtok函數將保存它在字符串中的位置。
  • strtok函數的第一個參數為NULL,函數將在同一個字符串中被保存的位置開始,查找下一個標記。
  • 如果字符串中不存在更多的標記,則返回NULL指針。

使用舉例:

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

int main()
{
	char arr[] = "abc@def.123@456";
	char buf[30] = { 0 }; // 使用strtok,一般要做備份
	strcpy(buf, arr);
	const char* sep = "@."; // 分隔符的集合

	char* str = NULL;
	for (str = strtok(buf, sep); str != NULL; str = strtok(NULL, sep))
	{
		printf("%s\n", str);
	}

	return 0;
}

6. 錯誤信息報告

6.1 strerror

char * strerror ( int errnum );

會返回錯誤碼對應的錯誤信息。

有一個全局變量errno,會記錄庫函數在調用失敗后的錯誤碼。使用時需要引用頭文件errno.h。

使用舉例:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>

int main()
{
	int* p = (int*)malloc(INT_MAX);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
	}

	return 0;
}

7. 字符操作函數

以下函數對應的頭文件是ctype.h。

7.1 字符分類函數

函數 如果它的參數復合下列條件就返回真
iscntrl 任何控制字符
isspace 任何空白字符
isdigit 十進制數字
isxdigit 十六進制數字
islower 小寫字母
isupper 大寫字母
isalpha 大小寫字母
isalnum 大小寫字母或數字
ispunct 標點符號
isgraph 圖形字符
isprint? 可打印字符

7.2 字符轉換函數

轉小寫:

int tolower( int c );

轉大寫

int toupper( int c );

8. 內存操作函數

8.1 memcpy

void * memcpy ( void * destination, const void * source, size_t num );

函數memcpy從source的位置開始向后復制num個字節的數據到destination的內存位置。

這個函數在遇到'\0'的時候并不會停下來。

如果source和destination有任何的重疊,復制的結果都是未定義的。

模擬實現如下:

void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;

	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}

8.2 memmove

void * memmove ( void * destination, const void * source, size_t num );

和memcpy的差別就是memmove函數處理的源內存塊和目標內存塊是可以重疊的。

如果源空間和目標空間出現重疊,就得使用memmove函數處理。

模擬實現如下:

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;

	if (dest < src)
	{
		// 前->后
		while (count--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		// 后->前
		while (count--)
		{
			*((char*)dest + count) = *((char*)src + count);
		}
	}

	return ret;
}

8.3 memcmp

int memcmp ( const void * ptr1,
             const void * ptr2,
             size_t num );

比較從ptr1和ptr2指針開始的num個字節。

返回值和strcmp類似,根據大小關系返回正數、負數或者0。

模擬實現如下:

int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
	assert(ptr1 && ptr2);

	while (num--)
	{
		if (*(char*)ptr1 > *(char*)ptr2)
		{
			return 1;
		}
		else if (*(char*)ptr1 < *(char*)ptr2)
		{
			return -1;
		}
		ptr1 = (char*)ptr1 + 1;
		ptr2 = (char*)ptr2 + 1;
	}

	return 0;
}

原文鏈接:https://blog.csdn.net/xiang_bolin/article/details/127638620

欄目分類
最近更新