李慧芹C语言学习笔记

一、数据类型等基本知识

二、输入输出

三、流程控制

四、数组

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);
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值