一、数据类型等基本知识
二、输入输出
三、流程控制
四、数组
1、一维数组
- 定义
- 初始化
static类型初始化:
static int arr[4];
//利用static初始化时,不同于auto,数组所有的元素将会全部初始化为0;
数字只能在初始化的时候直接赋值,不能再程序中直接赋值(可间接赋值)
int arr[4];
arr={1,2,3,4};
//上述操作是错误的,因为arr在初始化时是作为一
//个地址常量用来指向一组连续空间的,不能再对一个常量赋值;
//可以借助*p指针变量来进行操作;
- 数组越界
2、二维数组
- 定义
【存储类型】数据类型 标识符 【行下标】【列下标】
- 初始化
int arr[2][3]={{1,2,3},{4,5,6}};
//全部初始化
int arr[2][3]={{1},{4,6}};
//部分初始化,未初始化的部分为0,第一行为1,0,0;第二行为4,6,0;
int arr[2][3]={1,2,3,4};
//部分初始化,按照顺序赋值,第一行为1,2,3;第二行为4,0,0;
- 存储形式
顺序存储,按行存储
- 深入理解二维数组
int arr[2][3];
//那么arr就是一个指向a[0][0]的地址
arr+1;
//arr+1直接将arr偏移一行,arr+1指向a[1][0];
3、字符数组
- 定义
【存储类型】数据类型 标识符 【下标】
- 初始化
单个字符初始化
char str[3]={'a','b','c'};
//用单个字符初始化,未初始化的元素为‘\0’;
字符串初始化
char str[3]="a";
//利用字符串初始化时,字符会以‘\0’作为结束
//所以上初始化str[0]='a',str[0]='\0';
-
输入输出
用gets,puts
char str[32];
gets(str);
//gets不检查缓冲区,输入多个元素读多少个,无视初始化元素个数限制;
puts(str);
用scanf,printf
scanf("%s",str);
//利用%s输入,只能输入连续字符串,不能包含空格,tab等分隔符
printf(“%s”,str);
*-------------------------------------------------*
for(i=0;i<n;i++)
scanf("%c",&str[i]);
for(i=0;i<n;i++)
printf("%c",str[i]);
- 常用函数
strlen & sizeof
strlen以‘\0’作为结束标记,不包含‘\0’;
sizeof包含所有字符,包含‘\0’;
strcpy & strncpy
在strncpy中n一般取目标字符串数字的最大元素个数
strcat & strncat
连接字符串函数
strcmp & strncmp
逐位比较ascII码,strcmp(str,str1),返回str-str1的差值;strncmp比较前n位;
五、指针
1、变量与地址
变量名是用户对某一块地址空间的抽象表示,如int *p中,p就是变量名
指针就是地址,如int *p中,p的值就是地址
2、指针与指针变量
int i=1;
int *p=&i;
//指针描述了数据在内存中的位置&i,指针变量是用来存放地址的变量p
指针变量在64位系统中占空间为8个字节,32位系统中占4个字节
关于指针数据类型的区别:int *p意味着p+1跳转int大小个字节,float意味着p+1跳转float大小个字节。
3、直接访问与间接访问
4、空指针与野指针
空指针
int *p=NULL;
//将指针指向起始地址值为0x00的那一块空间,此时若对指针直接进行操作会段错误
//0x00这一块空间不分配给任何进程
野指针
int *p;
//随机分配了一个地址,禁止对未知地址区域进行任何操作
5、空类型
void *p=NULL;
//百搭类型,可与任意类型指针进行指令交换
6、定义与初始化的书写规则
7、指针运算
8、指针与数组
- 指针与一维数组
int arr[6]={1,2,3,4,5,6};
int *p=arr;
//可以用arr与p互相替换(指针变量的类型需要和数组元素类型相同)
// p+i<---->&arr[i] *(p+i)<---->arr[i]
arr++;
//这句话是不成立的,因为arr是一个地址常量,就相当于1=1+1;
p++;
//这句话是成立的,因为p是一个变量,p++则地址偏移一个整型
利用指针初始化数组,省略名字
int *p=(int [3]){1,2,3};
//不能直接int *p={1,2,3};
- 指针与二维数组
int arr[3][2]={1,2,3,4,5,6};
int *p=&arr[0][0];
//p必须对应一个与p同级的列指针
- 指针与字符数组
char str[]="I love china!";
char *p=str+7;
puts(p);
//如此一来,可以直接从str的第七位开始输出后面剩余的字符串
char str[]="hello";
str="world";
//这句话是错误的,因为str是数组名,是常量
strcpy(str,"world");
/**********************/
char *str="hello";
strcpy(str,"world");
//这句话是错误的,因为hello是一个常量,str只是一个地址变量用来表示这个常量的起始地址
str="world";
//可以让他指向world这个串的起始位置
/**********************/
//可以这样写
char str[]="hello";
char *strx=str;
strcpy(strx,"world");
9、const与指针
#define PI 3.14
//不检查语法,无脑替换
const float pi=3.14
//检查语法,改变值会报错误,必须在初始化时就赋值
int const p; const int p;
//常量
int const *p; const int *p;
//常量指针;指针指向地址中存储的值不能变,指针指向可以变;
int *const p;
//指针常量;指针的指向不能改变,但是指向地址中存储的值可以改变
const int *const p;
//指针指向与指针所指向目标变量的值都不能发生变化
- 如何改变const类型的指针或者变量:
假设初始化(i=0;*p=&i;)
当const修饰变量*p时,可以通过变量名i修改i的值
当const修饰i时,可以通过*p修改i的值,若有警告,可以用const修饰*p
- 如何记忆:
const修饰*p则值不变,修饰p则指向不变
10、指针数组与数组指针
- 数组指针:
【存储类型】数据类型 (*指针名)【下标】 = 值 如:int (*p)[3]---->int[3] *p
定义一个指针,指针每次移动int[3]个大小
- 指针数组:
【存储类型】 数据类型 * 数组名 【长度】 如:int * arr[ 3 ]----->int *[3] arr
一个数组,数组中的元素全部都是指针
11、多级指针
六、函数
1、函数的定义
数据类型 函数名 (【数据类型 形参名,数据类型 形参名,...】)
- main函数解析
int main (int argc,char *argv[])
//argc是一个计数器,计算传入参数的个数;*argv[]用来存储输入参数的值
- printf函数解析:
返回输出字符的个数,在linux中可以用echo $指令进行查询
2、函数的传参
- 值传递
- 地址传递
void swap(int *p,int *q)
{
int tmp;
tmp=*p;
*p=*q;
*q=tmp;
}
//通过间接引用改变主程序中变量的值
- 全局变量
3、函数的调用
- 嵌套调用
- 递归:求斐波那契数列
#include <stdio.h>
#include <stdlib.h>
int fib(int n)
{
if(n<1)
return 0;
if(n==1||n==2)
return 1;
return fib(n-1)+fib(n-2);
}
int main()
{
int n;
int res;
scanf("%d",&n);
res=fib(n);
printf("fib[%d]=%d",n,res);
}
4、函数与数组
- 函数与一维数组
传输一维数组的起始地址与元素个数,在子函数传参时如void print_arr(int p [ ],int n),这里的p只是一个地址,无需特别标记元素个数,在n中表示。
- 函数与二维数组
1)直接将二维数组起始地址与元素总个数传过去,&a[0][0]+行*列--->p+M*N
2)传输一个数组指针与行数列数,*a+行+列--->(*p)[N]+M+N
3)存储空间大小
int arr[M][N];
//占数组大小个空间,4*M*N
int (*p)[N];
//占指针大小个空间,8个字节
5、函数与指针
- 指针函数
返回值 * 函数名 (形参); 如:int * fun (int);
函数的返回值是指针类型
- 函数指针
类型 (*指针名) (形参); 如:int (*p)(int);
函数名是一段代码所关联的入口地址,函数指针就是一个指针指向一个函数,当引用函数时也可以借助指针来引用。
int add (int a,int b);
//这是一个子函数
int (*p) (int,int);
//这是一个函数指针
p=add;
//将指针指向函数的入口地址
- 函数指针数组
类型 (*数组名【下标】)(形参); 如:int (*arr[N])(int,int);
函数指针数组是一个数组,他的元素类型全部都是指向函数入口地址的指针
int add(int a,int b);
//一个函数
int sub(int a,int b);
//一个函数
int (*funcp[2])(int,int);
//一个函数指针数组
funcp[0]=add;
funcp[1]=sub;
//给数组赋值
七、构造类型
1、结构体
- 产生及意义
存储不同数据类型的数据
- 类型描述
struct 结构体名
{
数据类型 成员1;
数据类型 成员2;
......
};
不能再定义结构体时赋值,因为定义时不分配存储空间
- 嵌套定义
- 定义变量,初始化及成员引用
成员应用: 变量名.成员名
指针->成员名
(*指针).成员名
- 占用内存空间大小
假设结构体中第一个元素为int,那么只能在地址%sizeof(int)的地址才能开始存储另一个成员
- 函数传参
函数调用结构体时一般不使用结构体直接传参,而是使用指针,结构体是一个变量名,不是地址,所以在主函数中传参时要使用&;
struct std *p;
//结构体类型指针
2、共用体
- 产生及意义
很多成员公用一个存储空间
- 类型描述
union 共用体名
{
数据类型 成员名1;
数据类型 成员名2;
......
};
- 嵌套定义
对字节进行操作
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
union{
struct{
uint16_t i;
uint16_t j;
}x;
uint32_t y;
}a;
int main()
{
a.y=0x11223344;
printf("%x\n",a.x.i+a.x.j);
}
- 定义变量、初始化、成员引用
成员引用:变量名.成员名
指针名->成员名
- 占用内存大小
根据公用体中占空间最大元素进行内存分配
- 函数传参(值、地址)
- 位域
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
union{
struct{
char a:1;
char b:2;
char c:1;
}x;
char y;
}w;
int main()
{
w.y=1;
printf("%d\n",w.x.a);
exit(0);
}
//这里二进制转换为:取反->加一->加上符号位(1代表负数,0代表正数)
//反过来就是:去绝对值->减一->取反->加上符号位
3、枚举
- 定义
enum 标识符
{
成员1;
成员2;
......
};
元素依次递增,可以给任意元素赋值改变其递增起始值
八、动态内存管理
- malloc:
void *malloc(size_t size)
要size个字节空间
- realloc:
void *realloc(void *ptr,size_t size)
重新分配size大小空间,在原来空间ptr的基础上,如果ptr没有size这么大的连续空间,那么另外找符合条件的空间进行分配
- calloc:
void *calloc(size_t nmemb,size_t size)
要nmemb个成员,每个成员占size大小的空间
- free
通过变量p对于那一块空间已经没有引用权限,所以free后有必要把指针置为空
- 原则
谁申请谁释放
- typedef
对数据类型的定义
#define INT int
typedef int INT;
INT i --> int i;
#define IP int *
typedef int *IP;
IP p,q --> int *p,q;
//define无脑替换
IP p,q --> int *p,*q;
//typedef在指针部分的特殊作用
对结构体类型的定义
typedef int ARR[6];
ARR a; --> int a[6];
//即就是将int [6] ->ARR
struct node_st
{
int i;
float j;
};
1)typedef struct node_st NODE;
NODE a; -> struct node_st a;
2)typedef struct node_st *NODEP;
NODE p; -> struct node_st *p;
或者用node定义*node:NODE *p; ->struct node_st *p;
3)typedef struct
{
int i;
float j;
}NODE,*NODEP;
对函数类型的定义
1)typedef int FUNC(int); -> int(int) FUNC;
FUNC f; -> int f(int);
2)typedef int *FUNCP(int);
FUNCP p; -> int *p(int);
3)typedef int *(*FUNCP)(int);
FUNCP P; -> int *(*p)(int);
九、其他
1、学生管理系统
由于编译器版本不同,原版代码可能会报错,主要有以下两点:
1)sleep函数没有写头文件
2)使用malloc函数时需要加上强制转换将返回值类型从(void *)转换到所需要类型如(char *)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <windows.h>
#include <unistd.h>
#define NAMEMAX 1024
typedef struct student_st
{
int id;
char *name;
int math;
int chinese;
}STU;
void stu_set(STU *p,STU *q)
{
p->id=q->id;
p->name=(char*)malloc(strlen(q->name)+1);
if(p->name == NULL)
exit(1);
strcpy(p->name,q->name);
p->math=q->math;
p->chinese=q->chinese;
}
void stu_show(STU *p)
{
printf("%d %s %d %d\n",p->id,p->name,p->math,p->chinese);
}
void stu_changename(STU *p,const char *newname)
{
free(p->name);
p->name=(char*)malloc(strlen(newname)+1);
strcpy(p->name,newname);
}
void menu(void) {
printf("\n1 set\n2 change name\n3 show\n");
printf("Please enter the num(q for quit):");
}
int main() {
STU stu,tmp;
char newname[NAMEMAX];
int choice;
int ret;
do {
menu();
ret=scanf("%d",&choice);
if(ret!=1)
break;
switch(choice) {
case 1:
tmp.name=(char*)malloc(NAMEMAX);
printf("Please enter for the stu[id name math chinese]:");
scanf("%d%s%d%d",&tmp.id,tmp.name,&tmp.math,&tmp.chinese);
stu_set(&stu,&tmp);
break;
case 2:
printf("Please enter the newname:");
scanf("%s",newname);
stu_changename(&stu,newname);
break;
case 3:
stu_show(&stu);
break;
default:
break;
}
sleep(1);
} while(1);
stu_show(&stu);
exit(0);
}