前言
自己写来当笔记用的(内容不全),有需要可以看看,有错误的地方辛苦大家指正,谢谢
C语言中的数据类型主要分为以下几类:基本类型、指针类型、数组类型、结构类型、联合类型和枚举类型
头文件
return 0;
输入/输出
C语言没用专门存储字符串的变量类型,所以只能存储在char类型的数组中,如char name[40];
scanf函数遇到第一个空格,制表符,换行符就不再读取输入。
区别字符串常量和字符常量
字符串常量"x"和字符常量'x'
'x'是基本类型char,"x"是派生类型,char数组;
"x"实际上由两个字符组成,x和\0
printf()函数
整数
%2d,其对应的输出应该是两个字段宽度,但是待打印整数有三位数字,所有字段宽度自动扩大以符合整数的长度
%10d,对应的输出结果有10个空格宽度,在两个*号之间有7个空格和3位数字
%-10d,输出结果宽度同上,但是因为“-”标记,所以输出左对齐。
浮点数
%f默认打印整数和小数点后6位
%4.2f,指定小数部分只打印两位
第四,六两个都对结果进行了四舍五入
+标记会使得打印结果多一个+
0标记会使打印值前面以0进行填充满足字段要求
字符串格式
[%m.ns]其中m表示字段的宽度,n表示打印字符的个数
scanf()函数
规则:
"*"的使用
printf函数中的*
如果不想预先指定地段宽度,希望通过程序来实现,可以用*修饰符代替字段宽度
scanf函数中的*
getchar,putchar()
每次只处理一个字符
循环
do...while
条件运算符
指针
指针的值是它所指对象的地址, *用来取出指针所指地址的值
指着基本操作
查找地址 &运算符
通过使用“&”来获取变量的地址
int date = 11;
date 是变量, &date是变量的地址
取变量地址的时候只会取到第一个字节的地址,后续字节的地址会自动获取
%p是专门用来打印地址的 printf("%p\n",&date);
在C语言中,没有int* 这种类型,上图两行代码表达的意思是相同的,初始化指针p和int型数据q
int * pa = &date; pa是用来存放地址的,C语言中叫做指针变量
* 说明pa是指针(变量)
int 说明pa指向的对象是int类型的
同理: pc指向的数据是char类型的
* 在此处是解引用操作
指针传参(作为参数的指针)
指针的应用
1.数据的交换
如果传参不使用指针,那么函数外数据交换是无效的,只能通过指针来实现
例:
常见错误
定义的新的指针,但是没有指向任何对象就开始使用
指针类型
1.指针类型决定了:指针解引用的权限多大
2.指针类型决定了指针一步能走多长
不能使用类型准换把int型指针赋值给double型指针。
野指针
情况1:
上图运行会报错,报错非法访问内存
因为在方法test中会创建a的内存空间,在主函数main中也会创建*p的空间,但是函数test返回的a的地址会在函数结束的时候被释放掉,也就是该地址之前存在的a已经不存在。也是不允许访问的
情况2:
声明创建一个指针没有指向任何已存在对象,指向的是随机地址,是无法直接对这样的野指针进行直接赋值的
方法1:声明定义一个变量,将变量的地址给刚刚声明的指针
方法2:声明开辟一片新的空间
指针关系运算
指针+-整数
指针-指针
指针关系运算
指针进阶
数组与指针的关系
在C语言中,指针+1指的是增加一个存储单元,对数组而言+1后的地址就是下一个元素地址
上述的等价只是作为参数传参的时候效果是相同的。
如果输出char类型的字符,则输出的是字符串首字符地址对应的char数据,也就是h
如果输出字符串,只要有首字母地址就可以输出完整字符串
str1 = str2
str3与str4不等,它们指向常量字符串,内存中同一常量字符串只有一个(常量字符串: const)
二维数组和指针
如果使用一个指向二维数组的指针,尽量使用简单数组表示法不要使用指针表示法
指针数组
指针数组本质上是一个数组,只不过是指针修饰这个数组,意思是说这个数组的所有元素都是指针类型
数据类型 * 指针数组名 [N]
char *p[4]={"hello","world","nice","happy"};
p是定义的一个指针数组,它有四个元素,每个元素是一个char *类型的指针,这些指针存放着其对应字符串的首地址。 这就相当与定义char *p1 = "hello",char *p2 = "world",char *p3 = "nice", char *p4 = "happy",这是四个指针,每个指针存放一个字符串首地址,然后用p[4]这个数组分别存放这四个指针,就形成了指针数组。 p[0]就是取第一个字符串"hello",p[0][0]就是取第一个字符串的第一个元素'h'。
创建一个整型指针数组arr,数组数据类型为int* 类型的指针
数组可以通过数组首地址来访问,arr+i来访问数组中内容
也可以通过arr[][]的方式来模式实现二维数组,但非真正的二维数组
数组指针
是一种指针,是指向数组的指针 【整型指针 -- 指向整型的指针】
1.数组指针指向一维数组
数组名arr是数组首元素的地址,即arr[0]
要取出数组的地址&arr,而parr要存放数组地址,那么parr是一个指针,为指向数组的指针
int *parr是 指向 int 类型的指针,而&arr是数组类型的指针
故,parr是一个指针且指向数组
(*parr)说明parr是一个指针,(*parr)[]说明其指向的是数组,(*parr)[10]说明其指向的是数组有十个元素,也就是指向arr数组
数组指针指向数组的元素的数据类型是int故为:int (*parr)[10] = &arr;
【parr是指针的名字,int (*) [] 是数组指针的类型】
同上,double *d[5]; 存放&d的数组指针
明确数组指针 (*pd)[5]
数组指针指向数组的数据类型是 double*,故为 double* (*pd)[5] = &d;
例:
int a[9]=a{1,2,3,4,5,6,7,8,9};
int (*p)[9]=&a;//*p=a
int (*p)[9] = &a;这句表示指针变量p 指向数组a,p 为一个数组指针。&a表示数组的地址
&a与a不同 ,虽然两者的值一样,但是含义却大不相同:&a表示数组的地址,而a则表示数组首元素的地址。&a与a 经常会给我们造成混淆,所以要注意区分。
*p就等于a,也就是数组的首元素地址,所以*p+1就是数组第二个元素的地址。*(*p+1)就是取数组第二个元素2,以此类推。
数组名 VS &数组名
&数组名p2与p1的输出虽然是相同的,但是两者的种类完全不同
p1指向的是数组第一个元素,是int类型的数据
p2指向的是数组,p2为数组指针,指向的是一个数组
2.数组指针指向二维数组——行指针
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
int (*p)[4]=a;
二维数组的每一行都可以看作是一个一维数组,所以p 指向二维数组的第一行,p+1则指向二维数组的第二行,以此类推。
通过数组指针来取二维数组的元素:假设 i 代表二维数组的行,j 代表二维数组的列。
(1)p[i][j] (2)(*(p+i))[j] (3)*(p[i]+j)
(4)*((*(p+i))+j)
用指针传参来打印一个二维数组
将每一行作为一个数组来看待,每一行是一个大小为5的数组,一共有三个
方法1:用1个指针形参表示数组开始,用一个整数形参表明待处理个数
方法2:使用指针形参;两个指针
函数指针
函数指针本质上是一个指针,只不过指向的是一个函数。
字符串
字符串是C语言中最有用的数据类型之一
字符串常量属于静态存储类别,函数中使用字符串常量该字符串只会被存储一次
字符串常量:
字符串数组
在初始化数组的时候要确保数组元素个数比字符串长度多1(为了容纳空字符)
数组与指针的区别
初始化字符串数组来存储字符串和初始化指针来指向字符串有何区别
不能这样写的原因是因为赋值运算符左边必须是变量
指针和字符串
字符串读入 get 与 put
整行存储都被存储在words中
fget 与 fput函数
字符串相关函数
strlen() 统计字符串长度
strcat() 接受两个字符串作为参数,把第二个字符串的备份附加在第一个字符串的尾部
strcmp() 把用户的响应与已存储的字符串做比较
strcpy()和strcnpy()
储存
分配内存malloc和free
malloc()函数,接受一个参数:所需的内存字节数
malloc分配内存,但是不会为其赋命,但是会返回动态分配内存块的首字节地址
因此可以把该地址赋给一个指针变量,并使用指针来访问这块内存
结构体相关
结构体定义
一个结构是一个复合的结构类型,结构中有多个数据成员,使用一个变量来表达多个数据
将不同数据类型数据组合成一个有机的整体
定义结构体变量
例:
使用结构的时候,要按照 struct 结构名称 变量名称; 前面必须跟上struct,不得单独使用结构名称来声明变量。
结构体是一个类型,是一个模板,没有空间,不可以给结构体成员赋值,必须先创建变量
第一,三种都声明了结构point,第二种没有声明point,只是定义了两个变量。
指向结构的指针
如果使用结构体对象来操作成员,则使用.
如果使用结构体对象地址来进行操作则使用, ->
C语言中传参尽可能使用指针来进行传递(传一个结构不如传指针)
用下面的式子来表达结构date的变量myday中的成员month的数据
例:
声明指针 struct guy *him; 并未创建一个新的结构体,而是创建了一个可以指向任意现有的guy类型的结构
him = &follew[0];
用指针访问成员
结构传参
使用结构指针传参
传递结构(结构传参)
结构,指针,malloc
结构体数组
结构中的结构
结构体赋值
结构体指针
struct stu *p;
操作结构体指针使用 ->
结构体套指针
先声明结构体t
再声明一个新的结构体,在新结构体tea中声明结构体t类型的指针
新建变量中开辟了存贮空间,变量有了存储地址,id是可以直接赋值的
指针变量p可以选择直接赋值,也可以新建指定大小空间将地址赋值指针
结构体tea中包含的结构体变量因为是指针,所以必须malloc地址空间,
关于结构体指针(typedef struct后面加*指针)【转载】
原文:关于结构体指针(typedef struct后面加*指针)_typedef struct *-CSDN博客
typedef struct node
{
int data;
struct node *rchild,*lchild;
}node,*Node;
这里面的使用了typedef关键字,node就是替代了struct node的意思,而Node则代表了struct node*的意思,他指针指向了整个结构体
当你创建的是node T时,只能用T.data来表示数据
当你用Node指针创建时,Node T就可以用T->data来表示数据
代码来验证:
#include "stdio.h"
#include "stdlib.h"
typedef struct node
{
int data;
struct node *rchild,*lchild;
}node,*Node;
int aa(Node T)
//传进去的就是指向整个结构体的指针 可以用->直接调用出来
像这种相当于它在这函数里面又创了结构体只是把我们传进去的内容进行了替换
{
printf("aa data=%d\n",T->data);
T->data=456;
printf("aa改变后 data=%d\n",T->data);
Node *p;
p=(Node*)malloc(sizeof(Node));
*p=T;
printf("T的值是%p\n",T);
printf("T的地址是%p\n",&T);
printf("p的值是%p\n",p);
printf("p的地址是%p\n",&p);
// printf("aa改变后 data=%d\n",*(*p)->data);
}
int bb(Node *T)
//其实定义是个二级指针地址 要把它用*解析一层才能得到结构体指针 像这种传结构体指针的地址进去的
它是又创建了一个二级指针来存储,里面的结构体就是主函数创建的那个结构体
{
printf("bb data=%d\n",(*T)->data);
(*T)->data=789; //因为T是地址不是指针,要用*号解析
printf("bb改变后 data=%d\n",(*T)->data);
printf("T的值是%p\n",T); //因为传入的是指针的地址,所以T的值就是主函数T的地址
printf("T的地址是%p\n",&T); //这是子函数里 另创的结构体与主函数的结构体不同,所以地址不同
printf("T的里面的地址是%p\n",*T);
}
int main()
{
Node T=NULL;
T=(Node)malloc(sizeof(node));
T->data=123;
printf("T的值是%p\n",T);
printf("T的地址是%p\n",&T);
printf("data=%d\n",T->data);
printf("---------------------\n");
aa(T);
printf("aa改变后在main中 data=%d\n",T->data);
printf("---------------------\n");
bb(&T);
printf("bb改变后在main中 data=%d\n",T->data);
printf("---------------------\n");
printf("main中最终结果 data=%d\n",T->data);
}
结构体里面包含指向本结构体的指针
结构体中包含一个指向结构体类型的指针,该指针可以保存下一个该结构体类型结点的地址,可以用于实现链表,树,图等数据结构