C语言 08.字符串和内存

【求非空字符串元素个数,求一串字符串里,不是’\n’的字符的个数】:

int no_space(char* str)
{
    int count = 0;
    char* p = str;
    while (*p)
    {
        if (*p != ' ')
        {    
            count++;
        }
        p++;
    }
    return count;
}
int main(int argc,char* argv[])
{
    char str[] = "ni hao ma";
    int count = no_space(str);
    printf("%d\n", count);
}

【字符串逆置: str_inverse“:hello – olleh 】

void str_inserse(char *str)
{
    char *start = str;            // 记录首元素地址
    char *end = str + strlen(str) - 1;    // 记录最后一个元素地址。

    while (start < end)            // 首元素地址是否 < 最后一个元素地址
    {
        char tmp = *start;        // 三杯水 char 元素交换
        *start = *end;
        *end = tmp;
        start++;            // 首元素对应指针后移
        end--;                // 尾元素对应指针前移
    }
}

【判断字符串是回文】:

int converse(char* str)
{
    char* start = str;
    char* end = str + strlen(str) - 1;
    
    while (start<end)
    {
        while (*start != *end)      // 判断字符是否一致
        {
            return 0;               // 表示不一致
        }
        start++;
        end--;                      // 这里一定要是减法,不能是加法
    }
    return 1;
}
int main(int argc,char* argv[])
{
    char* str = "oooo";
    int a = converse(str);
    printf("%s\n", a == 1 ? "是回文数字" : "不是回文数字");
}

1.字符串处理函数: #include <string.h>

1.1字符串拷贝: strcpy:

将 src 的内容,拷贝给 dest。 返回 dest。 保证dest空间足够大。【不安全】
char *strcpy(char *dest, const char *src);
函数调用结束 返回值和 dest参数结果一致。返回值是一个char *

 char src[] = "hello";
 char dest[100] = { 0 };
 char *p = strcpy(dest, src);  // src 拷贝给dest
 printf("%s\n", p);

1.2 字符串拷贝 strncpy:

将 src 的内容,拷贝给 dest。只拷贝 n 个字节。 通常 n 与dest对应的空间一致。
默认 不添加 ‘\0’
char *strncpy(char *dest, const char *src, size_t n);
特性: n > src: 只拷贝 src 的大小

    char src[] = "hello";
    char dest[6] = { 0 };
    char *p = strncpy(dest, src,6);  // src 拷贝给dest
    printf("%s\n", p);

1.3 字符串拼接: strcat

将 src 的内容,拼接到 dest 后。 返回拼接后的字符串。 保证 dest 空间足够大。
char *strcat(char *dest, const char *src);

    char src[] = " world";
    char dest[] = " hello";
    char* p = strcat(dest, src);  // src拼接在dest后面    
    printf("%s\n", p);           // dest和p是一样的,所以要保证dest空间足够大

1.4 字符串拼接 strncat:

将 src 的前 n 个字符,拼接到 dest 后。 形成一个新的字符串。保证 dest 空间足够大(默认拼到dest里面了)。
char *strncat(char *dest, const char *src, size_t n);
函数调用结束 返回值和 dest 参数结果一致。

    char src[] = " world";
    char dest[] = " hello";
    char* p = strncat(dest, src,3);  // src的前n个字符拼接在dest后面    hello wo
    printf("%s\n", p);

1.5 字符串比较: strcmp 不能使用 > < >= <= == !=

比较s1和s2两个字符串,如果相等 返回0.如果不相等,进一步表 s1 和 s2 对应位 ASCII码值。
s1 > s2 返回1
s1 < s2 返回-1
int strcmp(const char *s1, const char *s2);
只比较对应位,不比较总的ASCII值

    char src[] = " b";
    char dest[] = " a";
    int a = strcmp(src,dest);  // 
    printf("%d\n", a);         // -1  a<b

1.6字符串比较 strncmp:

int strncmp(const char *s1, const char *s2, size_t n);
比较s1和s2两个字符串的前n个字符,
如果相等 返回0。如果不相等,进一步表 s1 和 s2 对应位 ASCII码值。(不比字符串ASCII码的和)
s1 > s2 返回1
s1 < s2 返回-1

1.7 字符串格式化输入、输出:

1.7.1sprintf(): s – string

            int sprintf(char *str, const char *format, ...);
            对应printf,将原来写到屏幕的“格式化字符串”,写到 参数1 str中。 (不在屏幕中打印了)
            printf("%d+%d=%d\n", 10, 24, 10+24);
            ---char str[100];
            sprintf(str, "%d+%d=%d\n", 10, 24, 10+24);  格式串写入str数组中。相比于printf多了一个str, 这一步不会输出,只有写入操作

1.7.2 int sscanf(const char *str, const char *format, …);

          对应scanf, 将原来从屏幕获取的“格式化字符串”, 从 参数1 str中 获取。
          scanf("%d+%d=%d", &a, &b, &c);
          ---char str[]= "10+24=45";
          sscanf(str, "%d+%d=%d", &a, &b, &c);  a --> 10, b --> 24, c --> 45

1.8字符串查找字符、子串:

       strchr():
   		            在字符串str中 找一个字符出现的位置。 返回字符在字符串中的地址。
   		            char *strchr(const char *s, int c);
   		            printf("%s\n" strchr("hehehahahoho", 'a'));  --> "ahahoho"

       strrchr():
   		            自右向左,在字符串str中 找一个字符出现的位置。 返回字符在字符串中的地址。
   		            char *strrchr(const char *s, int c);
   		            printf("%s\n" strrchr("hehehahahoho", 'a'));  --> "ahoho"
       strstr():
   		            在字符串str中,找子串substr第一次出现的位置。返回地址。
   		            char *strstr(const char *str, const char *substr);
   		            在字符串中找子串的位置。 
   		            printf("%s\n" strrchr("hehehahahoho", "ho"));  --> "hoho"
   		            printf("%s\n" strrchr("hehehahahoho", "xixi"));  --> NULL

scanf(“%s”, str);
scanf(“%[^\n]”, str); 正则表示式:可以接收除换行之外的所有字符

1.8字符串分割:

strtok(): 按照既定的分割符,来拆分字符串。“www.baidu.com” --> “www\0baidu.com”
char *strtok(char *str, const char *delim);
参1: 待拆分字符串
参2: 分割符组成的“分割串”,就是用什么来分割,以为www.baidu.com为例:strtok(“www.baidu.com”,“.”);
返回:字符串拆分后的首地址。 “拆分”:将分割字符用 '\0’替换。最后变成www baidu.com 两份
特性:
1)strtok拆分字符串是直接在 原串 上操作,所以要求参1必须,可读可写(char *str = “www.baidu.com” 不行!!!)
2)第一次拆分,参1 传待拆分的原串。 第1+ 次拆分时,参1传 NULL.

    char str[] = "www.itcast.cn";   // 这个是变量,char *str是变量
    int n = strlen(str);
    char* p = strtok(str, ".");            // 这里一定要是双引号,不然会报错      
    printf("p = %s\n", p);// 第一次拆分,参1传待拆分的原串
    while (p != NULL)
    {
        p = strtok(NULL, ".");                 // 第n+1次拆分,参数传NULL
        printf("p = %s\n", p);
    }
    for (size_t i = 0; i < n; i++)
    {
        printf("【%c】", str[i]);
    }
    /*
    p = www
    p = itcast
    p = cn
    p = (null)
    【w】【w】【w】【】【i】【t】【c】【a】【s】【t】【】【c】【n】
    */

【 练习: 拆分 “.itcast.cn$This is a strtok$test”】

        char str[] = "www.itcast.cn$This is a strtok$test";
        char *p = strtok(str, "$ .");
        while (p != NULL)
        {    
            p = strtok(NULL, " .$");
            printf("p = %s\n", p);
        }

1.9 atoi/atof/atol: a:字符串 i:整数 f: 浮点数 l:长整数

使用这类函数进行转换,要求,原串必须是可转换的字符串。
错误使用(不报错,强制转):(atoi)“abc123” --> 0; “12abc345” —> 12; “123xyz” -->123 转换的前提是字符串里面的内容是相应的格式,比如说整数,浮点数等等
atoi:字符串 转 整数。 int atoi(const char *nptr);
atof:字符串 转 浮点数
atol:字符串 转 长整数

    char str1[] = "10";
    int num1 = atoi(str1);
    printf("num1 = %d\n", num1);
    char str2[] = "0.123";
    double num2 = atof(str2);
    printf("num2 = %f\n", num2);
    char str3[] = "123L";
    long num3 = atol(str3);
    printf("num3 = %ld\n", num3);
    /*
    num1 = 10
    num2 = 0.123000
    num3 = 123
    */

1.10 字符串处理函数的实现

(1) 回文字符串

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>


int str_abcba(char *str)
{
	char *start = str;
	char *end = str + strlen(str) - 1;

	while (start < end)
	{
		if (*start != *end)
		{
			return 0;	// 不是回文。
		}
		start++;
		end--;
	}
	return 1;	// 是回文。
}

int main(void)
{
	char str[] = "hello world";
	
	int ret = str_abcba(str);
	if (ret == 1)
	{
		printf("是回文字符串\n");	
	}
	else
	{
		printf("不是回文字符串\n");	
	}

	return EXIT_SUCCESS;
}

(2)字符串比较strcmp_strncmp

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

int mystrcmp(const char* str1, const char*str2)
{
	if (!str1 || !str2)
		return -2;
		
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
	}
	
	return *str1 > *str2 ? 1 : -1;
}

int mystrncmp(const char* str1, char* str2, size_t size)
{
	if (!str1 || !str2)
		return -2;
		
	int i = 0;
	while (*str1 == *str2)
	{
		if (i == size || *str1 == '\0')
			return 0;
		i++;
		str1++;
		str2++;
	}
	
	return *str1 > *str2 ? 1 : -1;
}

int main(void)
{
	char str1[] = "hello world";
	char str2[] = "hello kitty";

	//int value = mystrcmp(str1, str2);
	int value = mystrncmp(str1, str2, 5);
	
	printf("%d\n", value);
	
	if (value == 0)
	{
		printf("相同\n");
	}
	else
	{
		printf("不相同\n");
	}
	
	return EXIT_SUCCESS;
}

(3) 字符串拷贝strcpy_strncpy

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

char* mystrcpy(char* dest, const char* src)
{
	char* temp = dest;
	
	if (!dest || !src)
		return NULL;
		
	while (*dest++ = *src++);
	
	return temp;
}

char* mystrncpy(char* dest, const char* src, size_t size)
{
	if (!dest || !src)
		return NULL;
		
	size_t i;
	
	for ( i = 0; i < size; i++)
	{
		*(dest+i) = *(src+i);
	}
	*(dest + i) = 0;
	
	return dest;
}
int main(void)
{
	char src[] = "hello world";
	
	char dst[100];

	//mystrcpy(dst, src);
	mystrncpy(dst, src, 5);
	
	printf("%s\n", dst);
	
	return EXIT_SUCCESS;
}

(4)字符串逆置

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

//字符串逆置
void str_inverse(char *str)
{
	char *start = str;
	char *end = str + strlen(str) - 1;

	while (start < end)
	{
		char temp = *start;
		*start = *end;
		*end = temp;

		start++;
		end--;
	}
}

int main(void)
{
	char str[] = "hello world";
	
	str_inverse(str);

	printf("%s\n", str);

	return EXIT_SUCCESS;
}

(5) 字符串长度strlen

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

int my_strlen(const char *str)
{
	char *p = str;

	//while (*p != '\0')
	while (*p)
	{
		p++;
	}

	return p-str;
}

int my_strlen2(const char *str)
{
	char *p = str;

	while (*p++);

	return p - str -1;
}

int main(void)
{
	char str[] = "hello world";

	//int len = my_strlen(str);
	int len = my_strlen2(str);

	printf("len = %d\n", len);

	system("pause");
	
	return EXIT_SUCCESS;
}


(6)字符串拼接strcat_strncat

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

char* mystrcat(char* dst, const char* src)
{
	char* temp = dst;
	if (!dst || !src)
		return NULL;

	while (*dst)dst++;

	while (*dst++ = *src++);

	return temp;

}

char* mystrncat(char* dst, const char* src, size_t size)
{
	char* temp = dst;
	
	if (!dst || !src)
		return NULL;

	while (*dst)dst++;
	int i;
	for (i = 0; i < size; i++)
	{
		*(dst + i) = *(src + i);
	}
	*(dst + i) = 0;
	
	return temp;
}

int main(void)
{
	char src[] = "world";
	char dst[100] = "hello";

	//mystrcat(dst, src);
	mystrncat(dst, src, 3);
	
	printf("%s\n", dst);
	
	return EXIT_SUCCESS;
}

(7)字符串找字符strchr

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

char *my_strchr(char *str, char ch)
{
	while (*str)
	{
		if (*str == ch)
			return str;
		str++;
	}
	return NULL;
}

char *my_strchr2(char *str, char ch)
{
	int i = 0;
	//while (str[i] != '\0')
	while (str[i])		// str[i] --> *(str+i)
	{
		if (str[i] == ch)
			return &str[i];
		i++;
	}
	return NULL;
}

int main(void)
{
	char str[] = "hello";
	char ch = 'l';

	char *p = my_strchr2(str, ch);

	if (p == NULL)
		printf("没有\n");
	else
		printf("%s\n", p);

	system("pause");
	return EXIT_SUCCESS;
}

(8)字符串找子串strstr

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

char *my_strstr(char *str, char *substr)
{
	char *pstr = str;		// 遍历 str 的指针
	char *temp = str;		// 记录回滚位置的指针
	char *psub = substr;		// 遍历 substr 的指针

	while (*pstr)			// 遍历 原字符串。	
	{
		temp = pstr;		// 记录位置									

		while (*pstr == *psub && *pstr != '\0')				
		{
			pstr++;			// 一起向后走
			psub++;			// 一起向后走
		}
		if (*psub == '\0')								
		{
			return temp;
		}
		// 回滚
		psub = substr;											
		pstr = temp;												

		pstr++;			// 向后走一个字符。								
	}
	
	return NULL;
}

int main(void)
{
	char str[] = "helhellolo world";
	char substr[] = "hello";
	
	printf("%s\n", my_strstr(str, substr));
	
	system("pause");
	
	return EXIT_SUCCESS;
}

2.变量及作用域

局部变量:

概念:定义在函数 内 部的变量。
作用域:从定义位置开始,到包裹该变量的第一个右大括号结束。

全局变量:

概念:定义在函数 外 部的变量。
作用域:从定义位置开始,默认到本文件内部。 其他文件如果想使用,可以通过声明方式将作用域导出。
extern a = 10; // 显式声明

static全局变量:

定义语法: 在全局变量定义之前添加 static 关键字。 static int a = 10;
作用域:被限制在本文件内部,不允许通过声明导出到其他文件。相对于全局变量,范围缩小了

static局部变量:

定义语法: 在局部变量定义之前添加 static 关键字。
特性: 静态局部变量只定义一次。在全局位置。 通常用来做计数器。(相当于全局变量的功能,但是作用范围缩小了)
作用域:从定义位置开始,到包裹该变量的第一个右大括号结束。

void test(void)
{
    int b = 10;
    printf("%d ", b++);
}
void test_static(void)
{
    static int a = 100;
    printf("%d ", a++);
}
int main(void)
{
    //printf("%d", a);        // 这里会报错,因为a是全局变量
    printf("-----------------没有 static-------------------------\n");
    for (size_t i = 0; i < 10; i++)
    {
        test();
    }
    printf("\n");
    printf("--------------------有 static --------------------------\n");
    for (size_t i = 0; i < 10; i++)
    {
        test_static();
    }
    /*
    -----------------没有 static-------------------------
    10 10 10 10 10 10 10 10 10 10
    --------------------有 static --------------------------
    100 101 102 103 104 105 106 107 108 109
    */
}

全局函数: 函数

定义语法: 函数原型 + 函数体

static函数:

定义语法:static + 函数原型 + 函数体
static 函数 只能在 本文件内部使用。 其他文件即使声明也无效。

【生命周期】:作用域是在哪里起作用,生命周期是活多久

局部变量:从变量定义开始,函数调用完成。 — 函数内部。 栈帧上
全局变量:程序启动开始,程序终止结束。 — 程序执行期间。
static局部变量:程序启动开始,程序终止结束。 — 程序执行期间。
static全局变量:程序启动开始,程序终止结束。 — 程序执行期间。
全局函数:程序启动开始,程序终止结束。 — 程序执行期间。
static函数:程序启动开始,程序终止结束。 — 程序执行期间。

3.内存4区模型:

在这里插入图片描述

代码段:.text段。 程序源代码(二进制形式)。
数据段:只读数据段 .rodata段。初始化数据段 .data段。 未初始化数据段 .bss 段。
stack:栈。 在其之上开辟 栈帧。 windows 1M — 10M Linux: 8M — 16M
heap:堆。 给用户自定义数据提供空间。 约 1.3G+

【命名冲突:就近原则】 不推荐大家这样写

int b = 10;
int main(void)
{
    int b = 100;
    printf("%d\n", b); // 100
} 

开辟释放 heap 空间:

void *malloc(size_t size); 申请 size 大小的空间
返回实际申请到的内存空间首地址。 【我们通常拿来当数组用】

void free(void *ptr); 释放申请的空间
参数: malloc返回的地址值。

使用 heap 空间:

空间时连续。 当成数组使用。
free后的空间,不会立即失效。 通常将free后的 地址置为NULL。
free 地址必须 是 malloc申请地址。否则出错。
如果malloc之后的地址一定会变化,那么使用临时变量tmp 保存。

    // int arr[1000000000] = { 0,1,2 };       // 这里开始报错,因为超过了栈的容量,需要转移到堆中
    int *p = (int *)malloc(sizeof(int) *10);
    if (p == NULL)
    {
        printf("malloc error\n");           // 申请失败
        return -1;
    }
char * tmp = p;    // 记录malloc的地址值,将来用于free操作
    // 写数据到malloc空间
    for (size_t i = 0; i < 10; i++)
    {
        p[i] = i + 10;
    }
    // 读出malloc空间中的数据
    for (size_t i = 0; i < 10; i++)
    {
        printf("%d    ",*(p+i) );           // 10    11    12    13    14    15    16    17    18    19
    }
p++;
    // 释放申请的内存
   // free(p);
free(temp);
     //p = NULL:

二级指针对应的 heap空间:

在这里插入图片描述

    int** p = malloc(sizeof(int *)*3);      //int **p ==> int  *p[10]; ==>  [int *, int *, int*]
    for (size_t i = 0; i < 3; i++)
    {
        p[i] = malloc(sizeof(int) * 5);
    }
    // 使用空间 -- 写
    for (size_t i = 0; i < 3; i++)
    {
        for (size_t j = 0; j < 5; j++)
        {
            p[i][j] = i + j;
        }
    }
    // 使用空间  -- 读
    for (size_t i = 0; i < 3; i++)
    {
        for (size_t j = 0; j < 5; j++)
        {
            printf("%d", *(*(p + i) + j)); //  p[i][j]==*(p+i)[j]== *(*(p+i)+j)        
        }
        printf("\n");
    }
    // 释放空间,应该先释放内层空间
    for (size_t i = 0; i < 3; i++)
    {
        free(p[i]);  // *(p+i)
        p[i] = NULL;  // 这一句可有可无
    }
    // 释放外层空间
    free(p);
    p = NULL;
    /*
    01234
    12345
    23456
    */
申请外层指针: char **p = (char **)malloc(sizeof(char *) * 5);
申请内层指针: for(i = 0; i < 5; i++)
        {
            p[i] = (char *)malloc(sizeof(char) *10);
        }
使用: 不能修改 p 的值。
     for(i = 0; i < 5; i++)
    {
        strcpy(p[i], "helloheap");
    }
释放内层:
     for(i = 0; i < 5; i++)
    {
        free(p[i]);
    }
释放外层z
    free(p);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值