1.字符串数据在C语言中的存储。
1).使用字符数组来存储。
将字符串数据的每一个字符,存储到字符数组中,并追加1个'0’代表存储结束,
char name[5] = {'j','a', 'c','k','\0'}; char name[] = {'j','a' 'c' 'k' '\0'};
char name[] ={"jack"}:只有直接给字符串数组初始化1个串的时候,才会自动追加1个'\0’,前提是字符数组的长度
足够的情况下。
char name[] = "jack";
2),使用字符指针来存储字符串数据。
直接将1个字符串数据 初始化 给1个字符指针。
const char* name = "jack";
注意:在VS中不能使用char *name="jack"。
*原因——“abcd”是字符串常量,不可更改,必须加上const用常量指针,即 const char *p = “abcd”
const char p="hello"分配在静态存储区,不可以改变.
char p[]="hello"分配在堆上,可以进行更改。
2二者储存有什么区别。
1)当他们都是局部变量的时候。
char name1[] ="jack";
const char *name2 = "rose";
a.name1字符数组。是申请在栈区.字符串的每一个字符存储在这个字符数组的每1个元素中。
b.name2是1个局部的指针变量,name2这个指针变量是声明在栈区的。字符串数据就是以字符数组的形式存储在常量区。
name2指针变量中,存储的是"rose"这个字符串在常量区的地址。
2)。当他们作为全局变量的时候。
char name1[] ="jack";
const char* name2 ="rose";
a,name1字符数组是存储在常量区的,字符串的 每1个字符是存储在这个数组中的每一个元素中,
b.name2指针也是存储在常量区的。字符串也是以字符数组的形式存储在常量区。
name2指针中存储的是"rose"这个字符串在常量区的地址。
3)这两种方式的区别:
在内存中存储的结构是不同的。
a,以字符数组存储:无论如何是1个字符数组。然后字符串的每一个字符存储在数组的元素之中,
b.以字符指针存储:无论如何首先有1个字符指针变量,字符串数据是以字符数组的形式存储在常量区的。
4)储存后是否可以修改数据?
a.以字符数组存储的字符串数据,可以修改字符数组的元素。
以字符数组的形式存储字符串数据,不管是全局的还是局部的,都可以使用下标去修改字符数组中的每一个元素。
b.以字符指针的形式存储字符串数据,这个时候字符指针指向的字符串数据是无法修改的,
以字符指针的形式存储字符串数据,不管是全局的还是局部的,都不能通过指针去修改指向的字符串数据。
3.内存中的五大区域
1).内存中分为五块区域。
a.栈
是专门用来存储局部变量的。所有的局部变量都是声明在栈区域中的。
b.堆
允许程序员手动的从堆申请空间来使用。 int 4
double 8
floar 4
char 1
程序员自己申请指定字节数的空间。
C.BSS段
是用来存储未初始化的全局变量和静态变量。声明1个全局变量。如果我们没有初始化。
在程序运行的最开始时候,这个全局变量是没有初始化的,存储在BSS段
d.数据段/常量区
用来存储已经初始化的全局变量、静态变量。还有常量数据,例int num=10 +20中的10和20.
e.代码段
用来存储程序的代码/指令。
4.字符串的恒定性。
大前提:是以字符指针形式存储的字符串。
1)。当我们以字符指针的形式存储字符串的时候,无论如何 字符串数据是存储在常量区的。
并且 一旦存储到常量区中去,这个字符串数据就无法更改。
2),当我们以字符指针的形式要将字符串数据存储到常量区的时候,并不是直接将字符串存储到常量区。而是先检查常量区中是否有相同内容的字符串,如果有,直接将这个字符串的地址拿过来返回。如果没有,才会将这个字符串数据存储在常量区。
3),当我们重新为字符指针初始化1个字符串的时候并不是修改原来的字符串,而是重新的创建了1个字符串,把这个新的字符串的地址赋值给它 。
4),最容易蒙圈的地方。
a.
char *name ="jack".
name ="rose"; 这样是可以的。
但是不是把"jack"改成了"rose”而是重新创建了1个"rose"把"rose"地址赋值给name
b. char name[] ="jk";
name ="rose";这样是不行的。name是数组名 代表数组的地址,不能为数组名赋值。
name[0] ='r'; name[1] ='o' name[2] ='s'; name[3] = 'e';name[4] ='\0'
这么做是可以的直接修改数组的元素
5.使用字符指针数组来存储多个字符串数据
char* names[4];这是1个一维数组每1个元素的类型是char指针
char* names[4] = {"jack" ,"rose""lily","lilei"};
names数组的元素的类型是char指针初始化给元素的字符串数据是存储在常量区的元素中存储的是 字符串在常量区的地址
优点:每1个字符串的长度不限制
#include<stdio.h>
int main()
{
const char* ch[4] = { "jalksjd","dhsa","aljs","ijdfliqaj" };
for (int i = 0; i < 4; i++)
{
printf("%s\n", ch[i]);
}}
6.字符串数组排序
冒泡排序
#include<stdio.h>
#include<string.h>
int main()
{
const char* countries[] = { "China","Mongolia","North Korea","Singapore","Indonesia","East Timor","Nepal","Bhutan","Bangladesh","Greece","Slovenia","Salvador","Barbados","Grenada","Trinidadand Tobago", "Nicaragua" };
//1. 先计算这个数组的长度
int len =sizeof(countries) / sizeof(countries[0]);
//2.使用冒泡排序
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - 1 - i; j++)
{
int res = strcmp(countries[j], countries[j + 1]);
if (res > 0) //说明j 比j+1小.
{
const char* temp = countries[j];
countries[j] = countries[j + 1];
countries[j + 1] = temp;
}
}
}
//3.打印结果
for (int i = 0; i < len; i++)
{
printf("%s\n", countries[i]);
}
}
选择排序
7.使用fputs函数将字符串输出到指定的流。
1)。fputs();
f --> File
作用:将字符串数据 输出到 指定的流中流:
标准输出流->控制台.
文件流 --> 磁盘上的文件
2)。使用格式:
A。fputs(要输出的字符串,指定的流);
要使用fputs函数将字符串数据输出到标准输出流
标准输出流:控制台.stdout
B。将字符串存储到文件中
a,要先声明1个文件指针.指向磁盘上的文件。
fopen函数可以创建1个指向文件的指针。
fopen函数的两个参数:
第1个参数:文件的路径,代表创建的指针就指向这个文件
第2个参数: 操作文件的模式,你要对这个文件做什么操作。必须要告诉他
“w”--> write 代表你要向这个文件写入内容
“r”-->read代表你从这个文件中读取数据
“a”--> apped 追加 代表你向这个文件中追加数据
当操作模式是"w"的时候,如果文件不存在,就会创建这个文件如果文件存在。就会将原来的文件替换掉,
当操作模式是”a"的时候.如果文件存在,则追加.如果不存在就创建这个文件
b,使用fputs函数将字符串写入到指定的文件流中
fputs(字符串,文件指针);
c写完之后,一定要记得使用fclose()函数 将这个文件关闭
#include<stdio.h>
int main()
{
//定义一个文件指针,创建一个文件,然后传入一个编写的数据
FILE* pfile = fopen("E:\Project1.txt", "a");
//定义一个字符串数据
const char* name = "love";
//,使用fputs函数将字符串写入到指定的文件流中
fputs(name, pfile);
//一定要记得使用fclose()函数
fclose(pfile);
printf("写入成功");
return 0;
}
8.使用fgets函数从标准输入流中读取数据
1)使用fgets函数从控制台接收用户输入字符串
前面学习的scanf函数gets函数也可以实现这个功能
scanf的缺点
a,不安全
b。输入的空格会被认为结束
gets函数的缺点
a.不安全
fgets函数的优点
a,安全
b,空格也会一并接收
语法: fgets(要将字符串存储到哪1个数组中,最多接收多少个长度的字符串,指定流);
第2个参数:我们写1个n 那么函数最多就接收n-1个长度的字符串
这个参数一般情况下和第1个参数数组的长度一致.
include<stdio.h>
int main()
{
char input[5];
printf("请输入");
fgets(input, 5, stdin);
printf("%s", input);
return 0;
}
stdin: 代表标准输入流,也就是键盘流 控制台输入
2)为什么是安全的?
a.如果我们输入的字符串的长度大于等于了 第2个参数n,只会接收前面的n-1个,然后最后1个自动是“\0'这样,就不会崩了。
b.如果我们输入的字符串的长度刚好等于n-1 那就是最完美的。
c.如果我们输入的字符串的长度小于了n-1,那么就会将我们最后输入的换行字符'\n'一并的接收然后,后面才是'0结束符。
解决方案:输入完毕之后,判断字符数组中存储的字符串最后1个是不是"n如果是'\n'那么就将其替换为'\0'
#include<stdio.h>
#include <string.h>
int main()
{
char input[5];
printf("请输入");
fgets(input, 5, stdin);
size_t len = strlen(input);
if (input[len - 1] == '\n')
{
input[len - 1] = '\0';
}
printf("%s", input);
return 0 ;
}
2)。使用fgets函数从文件流中读取数据:就是读取磁盘上文件的内容
//1创建1个读取文件的文件流
FILE* pFile = fopen("/Users/Itcast/Desktop/abc.txt","r")
b,准备1个字符数组.准备存储读取到的字符串数据
//char content[50]:
c,使用fgets函数从指定的文件流中读取
fgets(content,50,pFile);
d,读取成功:
printf("读取的内容是: %s\n",content);
e,关闭文件流
fclose(pFile);
#include<stdio.h>
int main()
{
//创建一个文件指针
FILE* pfile = fopen("E:Project1.txt", "r");
//重新声明一个数组,储存读取的数据
char content[50];
//使用fgets读取文件数据
fgets(content, 50, pfile);
//读取数据
printf("%s\n", content);
//关闭文件流
fclose(pfile);
return 0;
}
9.const关键词
A const修饰基本数据类型和数组
1),const是1个关键字.是来修饰我们的变量的也就是说在声明变量的同时,可以使用const关键字来修饰
const int num = 10:
一般情况下来说,被const修饰的变量具备一定程度上的不可变性
被const修饰的变量,叫做只读变量。
2).onst修饰基本数据类型的变量
基本数据类型: int、double、float、char.
a。 const int num = 10;
这个时候.num变量的值只能去取值,而不能去修改
b。int const num = 10;
效果同上
3)const修饰数组
a。const int arr[4] = {10,20,30,40};数组的元素的值不能修改
b。int const arr[4] = {10,20,30,40];
效果同上,
B.const修饰指针
1). const int* p1 = #
无法通过p1指针去修改指针指向的变量的值,但是如果直接操作变量这是可以的但是指针变量的值可以改,可以把另外1个变量的地址赋值给这个指针.
2). int const * pl = #
效果同上
3).int * const pl = #
p1的值不能修改,但是可以通过p1去修改p1指向的变量的值
简单的说就是p1不能指向其他地址,但可以修改原来指向地址的值
4). int const * const pl = #
既不能修改p1的值,也不能通过p1去修改p1指向的变量的值
C使用场景:当不想改变值的时候可以使用
10.malloc函数
0)。作用: 向堆空间申请指定字节的空间来使用
1),参数只有1个:size_t类型的 也就是unsigned long.
参数代表的意义: 向堆内存申请多少个连续的字节空间
2),做的事情: 在堆内存中申请连续的参数个字节空间,
3),返回值是: void *
void *代表没有类型的指针
返回的是创建的空间中第1个字节的地址
地址没有类型的,只是返回了第1个字节的地址,没有说这个指针是什么类型的.
4),我们应该使用什么类型的指针变量来保存malloc函数返回的地址呢?
那就要看你想要如何去操作申请的这些字节空间,
如果你想要1个字节1个字节的操作,那么就使用char指针.
如果你想要4个字节4个字节的操作,并且当做整型来操作 那么就是要int指针如果你想要8个字节8个字节的操作,那么就是要double指针,
如果你想要4个字节4个字节的操作,并且当做单精度浮点型来操作 那么就是要float指针
就要看你想如何操作申请的这些字节空间
int*p1= (int*)malloc(5);
*p1 = 100;
操作的时候是以4个字节为基本单位
char* p1 =(char*) malloc(8);
*p1 = 100;
操作的时候是以1个字节为基本单位
5)。在堆区申请的字节空间是从低地址向高地址分配每次申请的字节地址都是从0开始,
每一次申请的字节空间不一定挨在一起
但是,每一次申请的指定个字节, 这些字节一定肯定决定是连续的
6)。在堆区申请的字节,里面是有值的,值是垃圾值,不会自动清零
7)。在向堆区申请字节空间的时候,有可能会申请失败,如果申请失败 返回的指针就是NULL值
我们申请完空间之后,最好是判断一下.判断是否申请成功
int* p1 = (int*)malloc(4);
if(p1)
{
//代表申请成功,要做的事情放这就可以了.
}
8),申请的空间使用完毕之后,一定要记得释放
释放申请的堆空间:
free(指针);
如果没有free 程序结束才会释放,
记住,一定要释放。 因为我们的程序有可能会运行很久。
如果非要等到结束的时候自动释放 那就晚了.
11.
A calloc函数
1)。作用: 向堆区申请指定字节数的空间
2)。格式:
参数1: 多少个单位
参数2: 每1个单位的字节数
calloc(4,sizeof(int));表示申请4个int空间
3)。与malloc的优势
calloc申请的字节,申请完之后,系统会将字节中的数据清零
B realloc函数
1)。作用: 扩容 realloc(地址,容量)
2)。注意: 我们有了指针 几乎就可以操作内存上的每一个字节但是我们还是建议,不要乱来,只操作我们申请的字节空间,因为,有可能会出现一些问题
3)当我们发现我们之前在堆区申请的字节空间不够用的时候就可以使用realloc函数来为我们的字节空间扩容,
a,如果原来的空间后面还有剩余的空间。并且足够扩容。那么直接就扩容在屁股后面,
如果原来的空间后面有剩余空间但是剩下的空间不够扩容。就重新找1块足够的空间申请。将原来的数据拷贝过来,原来的空间被自动释放
标签:存储,const,字节,char,详解,字符串,指针 From: https://blog.51cto.com/u_15809685/5824778