第七章
初见函数(求素数的例子)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i9XwX3MY-1684216775691)(…/TyPircture/1667286913284.png)]
求和函数
#include<stdio.h>
#include<stdlib.h>
void sum(int begin,int end){
int i;
int sum=0;
for (i=begin;i<=end;i++){
sum+=i;
}
printf("%d to %d sum is %d\n",begin,end,sum);
}
int main()
{
sum(1,10);
sum(20,30);
sum(35,45);
system("pause");
return 0;
}
函数的定义与调用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kbnOgwmJ-1684216775692)(…/TyPircture/1667287660669.png)]
函数原型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UOxsFSRx-1684216775693)(…/TyPircture/1667288713771.png)]
如果把函数放到下面去,有些编译器可能会报错
解决办法:在头文件下先进行函数的原型声明,告诉编译器这个函数长什么样
#include<stdio.h>
#include<stdlib.h>
void sum(int begin,int end);//函数的声明
int main()
{
sum(1,10);
sum(20,30);
sum(35,45);
system("pause");
return 0;
}
void sum(int begin,int end){ //函数的定义
int i;
int sum=0;
for (i=begin;i<=end;i++){
sum+=i;
}
printf("%d to %d sum is %d\n",begin,end,sum);
}
参数传递
**调用函数的时候,是用表达式的值来初始化函数的参数 **
#include<stdio.h>
void swap(int a,int b);
int main(){
int a = 5;
int b = 6;
swap(a,b);
printf("a=%d b=%d\n",a,b);
return 0;
}
void swap(int a,int b){
int t = a;
a=b;
b=t;
}
//这样的代码并不能完成输出a与b的值的交换
如果函数有参数,调用函数时必须传递给它数量、类型正确的值
类型不匹配?:调用函数时给的值与参数的类型不匹配是c语言传统上最大的BUG,编译器总是悄悄帮你把类型转换好,但可能结果不是我们所期望的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cnt7RkWn-1684216775693)(…/TyPircture/1667290376917.png)]
本地变量
生存期与作用域的概念
1.生:什么时候这个变量开始出现了,到什么时候它消亡了
2.作:在什么范围内可以访问这个变量(这个变量可以起作用)
对于本地变量,这两个问题的答案是统一的:大括号内——块
Not found in current context 没有找到这个变量的值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pr5irF4T-1684216775694)(…/TyPircture/1667291341754.png)]
第八章
初试数组
array:数组
#include<stdio.h>
#include<stdlib.h>
int main(){
int x;
double sum = 0;
int cnt= 0;
int number[100];
scanf("%d",&x);
while (x!= -1){
number[cnt] = x;
sum += x;
cnt++;
scanf("%d",&x);
}
if (cnt>0){
int i;
double average = sum/cnt;
for ( i = 0; i < cnt; i++)
{
if (number[i]>average){
printf("%d\n",number[i]);
}
}
}
system("pause");
return 0;
}
segmentation fault数组越界
统计个数
题目:输入数量不确定的[0,9]范围内的整数,统计每一种数字出现的次数,输入-1表示结束
#include<stdio.h>
#include<stdlib.h>
int main(){
const int number = 10;
int x;
int count[number]; //int count[number]={0};
int i;
//初始化
for (i=0;i<number;i++){
count[i] = 0;
}
scanf("%d",&x);
while (x != -1){
if (x>=0 && x<=9){
count[x] ++;
}
scanf("%d",&x);
}
for (i=0;i<10;i++){
printf("%d:%d\n",i,count[i]);
}
system("pause");
return 0;
}
数组运算
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MLzvZSB9-1684216775695)(…/TyPircture/1667295491366.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IPa3JivR-1684216775695)(…/TyPircture/1667295534860.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uyDoopS2-1684216775696)(…/TyPircture/1667295634109.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WLz5V0F9-1684216775697)(…/TyPircture/1667295756831.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aRykawV1-1684216775697)(…/TyPircture/1667295977113.png)]
数组作为函数参数时,往往必须再用一个参数来传入数组的大小 (length),因为:1.不能在[]中给出数组的大小,不能再利用sizeof来计算数组的元素个数
数组的例子
先Pass后续再补
二维数组
Pass
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rHcNeLMC-1684216775698)(…/TyPircture/1667297666580.png)]
第九章 指针
如果能够将取得的变量的地址传递给一个函数,能否通过这个地址在那个函数内访问这个变量
一个参数能够保存别的变量的地址,如何表达能够保存地址的变量
int i;
int* p = &i;
int* p,q; //与下一句意思相同:表示p为一个指针
int *p,q;
作为参数的指针
void f(int *p);
//在被调用的时候得到了某个变量的地址;
int i = 0;f(&i);
//在函数里面可以通过这个指针访问外面的这个i
#include<stdio.h>
void f(int *p);
void g(int k);
int main(){
int i = 6;
printf("&i=%p\n",&i);
f(&i);
g(i);
return 0;
}
void f(int *p){
printf(" p=%p\n",p);
printf("*p=%d\n",*p);
*p = 26;
}
void g(int k){
printf("k=%d\n",k);
}
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yf1fcA06-1684216775699)(…/TyPircture/1667375413874.png)]
指针的使用
在之前函数那节有关于数的交换问题,出了swap函数
#include<stdio.h>
#include<stdlib.h>
void swap(int *pa,int *pb);
int main(void){
int a = 5;
int b = 6;
swap(&a,&b);
printf("a=%d,b=%d\n",a,b);
system("pause");
return 0;
}
void swap(int *pa,int *pb){
int t = *pa;
*pa = *pb;
*pb = t;
}
函数返回多个值,某些值就只能通过指针返回
传入的参数实际上是需要保存带回的结果的变量
#include<stdio.h>
#include<stdlib.h>
void minmanx(int a[],int len,int *max,int *min);
int main(){
int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,16,17,21,23,55};
int min,max;
minmanx(a,sizeof(a)/sizeof(a[0]),&min,&max);
printf("min=%d,max=%d\n",min,max);
system("pause");
return 0;
}
void minmax(int a[],int len,int *min,int *max){
int i;
*min = *max=a[0];
for (i=1;i<len;i++){
if (a[i]<*min){
*min = a[i];
}
if(a[i]>*max){
*max = a[i];
}
}
}
用函数状态中的return返回确认,而实际的值通过指针参数返回,好处:容易把函数的返回结果放到if条件判断中去
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OCNZdaLp-1684216775699)(…/TyPircture/1667377075745.png)]
#include<stdio.h>
#include<stdlib.h>
/**
* @return 如果除法成功返回1,否则返回0
*/
int divide(int a,int b,int *result);
int main(void){
int a=5;
int b=2;
int c;
if(divide(a,b,&c) ){
printf("%d/%d=%d\n",a,b,c);
}
system("pause");
return 0;
}
int divide(int a,int b,int *result){
int ret = 1;
if(b==0) ret=0;
else {
*result = a/b;
}
return ret;
}
指针最常见的错误
定义了指针变量,还没有指向任何变量,就开始使用指针
任何一个地址变量没有被赋值之前,没有得到任何变量的或者实际变量的地址之前,不能通过它去访问任何
int *p = 0;
int k;
k=12;
*p=12;
//错误的指针用法
指针与数组
为什么数组传进函数后的sizeof 不对了??
因为int a[] 就是个指针 只是看上去像个数组
函数参数表中的数组实际上是指针
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VFzcCRB3-1684216775700)(…/TyPircture/1667378950067.png)]
数组变量就是特殊的指针
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LevjBVtq-1684216775700)(…/TyPircture/1667379456290.png)]
指针运算 *
sizeof(char)=1,sizeof(int)=4
#include<stdio.h>
int main(){
char ac[] = {0,1,2,3,4,5,6,7,8,9,};
char *p = ac;
printf("p =%p\n",p);
printf("p+1=%p\n",p+1);
int ai[] = {0,1,2,3,4,5,6,7,8,9,};
char *q = ai;
printf("q =%q\n",q);
printf("q+1=%q\n",q+1);
return 0;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SCP6iPmx-1684216775701)(…/TyPircture/1667380685438.png)]
当要一个指针+1的时候,其实加的是一个sizeof
//接上
printf("*(p+1)=%d\n",*(p+1));//1
printf("*(q+1)=%d\n",*(q+1));//1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vIwtTXOH-1684216775702)(…/TyPircture/1667381169887.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7PHcnBli-1684216775702)(…/TyPircture/1667381459042.png)]
给指针做减法,不是直接把它们指向的值拿出来做减法,而是计算两个元素之间中间相隔了多少个sizeof
*p++
取出p所指的那个数据来,完事之后顺便把p移动到下一个位置去,*的优先级虽然高,但无++高,常用于数组类的连续空间操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eefZIXMn-1684216775703)(…/TyPircture/1667381838761.png)]
指针比较
<,<=,==,>,>=,!= all can do to pointer
比较它们在内存中的地址
数组中的单元地址肯定是线性递增的
指针的类型
无论指向什么类型,所有的指针的大小都是一样的因为都是地址,但是指向不同类型的指针是不能直接互相赋值的
用指针做什么
1、需要传入较大的数据时用作参数
2、传入数组后对数组做操作
3、函数返回不止一个结果
4、需要用函数来修改不止一个变量
5、动态申请的内存
动态内存分配
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xcoLq3z3-1684216775703)(…/TyPircture/1667383690164.png)]
#include<stdio.h>
#include<stdlib.h>
int main(){
int number;
int* a;
int i;
printf("输入数量:");
scanf("%d",&number);
//int a[number];
a = (int*)malloc(number*sizeof(int));
for (i=0;i<number;i++){
scanf("%d",&a[i]);
}
for (i=number-1;i>=0;i--){
printf("%d ",a[i]);
}
free(a);
system("pause");
return 0;
}
free() 把申请得来的空间还给“系统”,申请过来的空间,最终都应该要还,只能还申请来的空间的首地址
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5rXnsNpq-1684216775704)(…/TyPircture/1667384379410.png)]
第十章
字符串变量
用指针是指向原本数据本身,是不能修改的。用数组,其实是把数据拷贝进去,做了一个备份,对备份是可以修改的
char* s = "Hello,world!";
char s[] = "Hello,world!";
#include<stdio.h>
#include<stdlib.h>
int main(){
int i = 0;
char *s = "Hello,World";
char *s2 = "Hello,World";
char *s3 = "Hello,World";
printf("&i=%p\n",&i); //%p是按地址输出
printf("s =%p\n",s);
printf("s2=%p\n",s2);
printf("s3=%p\n",s3);
s3[0] = 'B';
printf("Here s3[0]=%c\n",s3[0]); //%c是按char输出
system("pause");
return 0;
}
当需要一个字符串的时候,是用指针还是数组
数组:这个字符串在这里,作为本地变量空间自动被回收
指针:这个字符串不知道在哪里,处理参数,动态分配空间
如果要构造一个字符串—数组
如果要处理一个字符串—指针
字符串的输入输出
#include<stdio.h>
void f(void){
char word[8];
char word2[8];
scanf("%7s",word);
scanf("%7s",word2);
printf("%s##%s##\n",word,word2);
}
/*在%和s之间的数字表示最多允许读入的字符的数量,这个数字应该比数组的大小小一
*/
int main(){
f();
return;
}
常见错误
char *string
scanf("%s",string);
/*以为char*是字符串类型,定义了一个字符串类型的变量string就可以直接用,由于没有对string初始化为0,所以不一定每次运行都出错*/
字符串数组
用一个数组去表达很多字符串??
char *a[]
//基础类型
#include<stdio.h>
int main(void)
{
int mouth;
char mouths[][10]={"January","February","March","April","May","June","July","August","September","October","November","December"};
printf("请输入月份:");
scanf("%d",&mouth);
printf("%s",mouths[mouth-1]);
return 0;
}
//递归判断输入是否正确
#include<stdio.h>
int judge(int *mouth);
int main(void)
{
int mouth;
char mouths[][10]={"January","February","March","April","May","June","July","August","September","October","November","December"};
printf("请输入月份:");
scanf("%d",&mouth);
judge(&mouth);
printf("%s",mouths[mouth-1]);
return 0;
}
int judge(int* mouth)
{
if(*mouth<13&&*mouth>0)
return;
else{
printf("还有%d月?\n",*mouth);
printf("请输入有关地球的月份(1月-12月之间):\n");
scanf("%d",mouth);
judge(mouth);
}
}
//读取
#include<stdio.h>
int Readmouth();
int main(void)
{
int mouth;
char mouths[][10]={"January","February","March","April","May","June","July","August","September","October","November","December"};
printf("请输入月份:");
mouth=Readmouth();
printf("%s",mouths[mouth-1]);
return 0;
}
int Readmouth()
{
int mouth;
while(scanf("%d",&mouth)==1){
if(mouth>0&&mouth<13)
return mouth;
else{
printf("%d月?程序只能读取(1-12)月\n"
"给你重新组织语言的机会:\n",mouth);
}
}
}
程序参数
int main(int argc,char const *argv[])
#include<stdio.h>
int main(int argc,char const *argv[]){
int i;
for(i=0;i<argc;i++){
printf("%d:%s\n",i,argv[i]);
}
return 0;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QRwIU6eE-1684216775704)(…/TyPircture/1667467795287.png)]
字符串函数
strlen
size_t strlen(const char *s);
返回s的字符串长度(不包括结尾的0)
#include<stdio.h>
#include<string.h>
size_t mylen(const char* s){
int idx = 0;
while(s[idx] != '\0'){
idx++;
}
return idx;
}
int main(){
char line[] = "Hello";
printf("strlen=%lu\n",strlen(line)); //printf("strlen=%lu\n",mylen(line));
printf("sizeof=%lu\n",sizeof(line));
return 0;
}
//strlen=5 sizeof=6
strcmp
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int mycmp(const char* s1,const char* s2){
while( *s1 == *s2 && *s1 !='\0'){
s1++;
s2++;
}
return *s1 - *s2;
/*
int idx = 0;
while ( s1[idx] == s2[idx] && s1[idx] !='\0'){
idx ++;
}
return s1[idx] - s2[idx];
*/
}
int main(int argc,char const *argv[]){
char s1[] = "abc";
char s2[] = "abc";
printf("%d\n",mycmp(s1,s2)); //printf("%d\n",strcmp(s1,s2));
printf("%d\n",'a'-'A');
return 0;
}
strcpy
char *strcpy(char *restrict dst,const char *restrict src);
//把src的字符串拷贝到dst
//restrict表明src和dst不重叠
复制一个字符串
char *dst = (char*)malloc(strlen(src)+1);
strcpy(dst,src);
//+1是为了把最后面的\0加进来
#include<stdio.h>
#include<string.h>
char* mycpy(char* dst,const char* src){
int idx = 0;
while (src[idx] != '\0'){
dst[idx] = src[idx];
idx++;
}
dst[idx] = '\0';
return dst;
/*
char* ret = dst; //因为后面dst一直在++需先保存
while(*src != '\0'){
*dst++ = *src++;
}
*dst = '\0';
return ret;
*/
}
int main(int argc,char const *argv[]){
char s1[] = "abc";
char s2[] = "abc";
strcpy(s1,s2);
return 0;
}
strcat
char *strcat(char *restrict s1,const char *restrict s2);
//把s2拷贝到s1的后面接成一个长字符串,返回s1
字符串搜索函数
char *strchr(const char *s,int c);
char *strrchr(const char *s,int c);
//返回NULL 表示找无
char *strstr(const char *s1,const char *s2);//在字符串中寻找单个字符串
char *strcasestr(const char *s1,const char *s2);//忽略大小写
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int arcg,char const *argv[]){
char s[] = "hello";
char *p = strchr(s,'l');
p = strchr(p+1,'l');//lo
printf("%s\n",p); //llo
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int arcg,char const *argv[]){
char s[] = "hello";
char *p = strchr(s,'l');
char *t = (char*)malloc(strlen(p)+1);
strcpy(t,p);
printf("%s\n",t); //llo
free(t);
}
找到l,要前面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9zKiraOe-1684216775705)(…/TyPircture/1667474702594.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PPvw52DB-1684216775705)(…/TyPircture/1667474784409.png)]
第十一章
枚举
枚举是一种用户定义的数字类型
enum 枚举类型 名字{名字0,…,名字n};
常量符号化
#include<stdio.h>
const int red = 0;
const int yellow = 1;
const int green = 2;
int main(int argc,char const *argv[]){
int color=-1;
char *colorName = NULL;
printf("输入你喜欢的颜色代码:");
scanf("%d",&color);
switch(color){
case red:colorName = "red";break;
case yellow:colorName = "yellow";break;
case green:colorName = "green";break;
default :colorName = "unknown";break;
}
printf("your like color is %s\n",colorName);
return 0;
}
套路:自动计数的枚举
#include<stdio.h>
enum COLOR {RED,YELLOW,GREEN,NumCOLORS};
//这样需要遍历所有的枚举量或者需要建立一个用枚举量做下标的数组就方便。
int main(int argc,char const *argv[]){
int color = -1;
char *ColorNames[NumCOLORS] = {"red","yellow","green",};
char *colorName = NULL;
printf("please enter your like color:");
scanf("%d",&color);
if( color >=0 && color <NumCOLORS ){
colorName = ColorNames[color];
}else {
colorName = "unknown";
}
printf("your like color is %s\n",colorName);
return 0;
}
结构类型
用一个变量去表达多个数据
声明结构类型
#include<stdio.h>
struct date{
int month;
int day;
int year;
};
int main(int argc,char const *argv[])
{
struct date today;
today.month = 12;
today.day = 07;
today.year = 2001;
printf("");//输出用%i >> today.xxx
return 0;
}
和本地变量一样,在函数内部声明的结构类型只能在函数内部适用,通常在外部声明结构类型
声明结构的形式
struct point{
int x;
int y;
};
//第二种形式没有声明point,只是定义了两个变量
struct{
int x;
int y;
}p1,p2;
struct{
int x;
int y;
}p1,p2;
结构初始化
#include<stdio.h>
struct date{
int month;
int day;
int year;
};
int main(int argc,char const *argv[])
{
struct date today = {07,31,2004};
struct date thismonth = {.month=7,.year=2014};
printf("Today date is %i-%i-%i.\n",today.year,today.month,today.day);
printf("This month is %i-%i-%i.\n",thismonth.year,thismonth.month,thismonth.day);
return 0;
}
结构运算
对于整个结构,可以做赋值、取地址,也可以传递给函数参数
结构指针
和数组不同,结构变量的名字并不是结构变量的地址,必须使用&运算符
struct date *pDate = &today;
结构与函数
结构作为函数参数
整个结构可以作为参数的值传入函数
这个时候是在函数内新建一个结构变量,并复制调用者的结构的值,也可以返回一个结构
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
struct date
{
int month;
int day;
int year;
};
bool isLeap(struct date d);
int numberOfDays(struct date d);
int main(int argc, char const *argv[]){
struct date today,tomorrow;
printf("Enter today date(mm dd yyyy):");
scanf("%i %i %i",&today.month,&today.day,&today.year);
if (today.day != numberOfDays(today)) {
tomorrow.day = today.day+1;
tomorrow.month = today.month;
tomorrow.year = today.year;
} else if(today.month ==12){
tomorrow.day = 1;
tomorrow.month = 1;
tomorrow.year = today.year+1;
} else{
tomorrow.day = 1;
tomorrow.month = today.month+1;
tomorrow.year = today.year;
}
printf("Tomorrow date is %i-%i-%i.\n",tomorrow.year,tomorrow.month,tomorrow.day);
system("pause");
return 0;
}
int numberOfDays(struct date d){
int days;
const int daysPerMonth[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
if(d.month ==2 &&isLeap(d)){
days = 29;
}else {
days = daysPerMonth[d.month-1];
}
return days;
}
bool isLeap(struct date d){
bool leap = false;
if ((d.year %4==0 && d.year %100 !=0)||d.year%400==0){
leap = true;
return leap;
}
}
输入结构
用指针去传入
类型定义
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aQ9chvg1-1684216775706)(…/TyPircture/1667631813891.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eGucbUd7-1684216775707)(…/TyPircture/1667631941826.png)]
联合
低位在前,小端存储
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2jPM9Tbt-1684216775707)(…/TyPircture/1667632452181.png)]
1234 —0x04D2
if (today.day != numberOfDays(today)) {
tomorrow.day = today.day+1;
tomorrow.month = today.month;
tomorrow.year = today.year;
} else if(today.month ==12){
tomorrow.day = 1;
tomorrow.month = 1;
tomorrow.year = today.year+1;
} else{
tomorrow.day = 1;
tomorrow.month = today.month+1;
tomorrow.year = today.year;
}
printf("Tomorrow date is %i-%i-%i.\n",tomorrow.year,tomorrow.month,tomorrow.day);
system("pause");
return 0;
}
int numberOfDays(struct date d){
int days;
const int daysPerMonth[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
if(d.month ==2 &&isLeap(d)){
days = 29;
}else {
days = daysPerMonth[d.month-1];
}
return days;
}
bool isLeap(struct date d){
bool leap = false;
if ((d.year %4==0 && d.year %100 !=0)||d.year%400==0){
leap = true;
return leap;
}
}
**输入结构**
用指针去传入
~~~c
类型定义
[外链图片转存中…(img-aQ9chvg1-1684216775706)]
[外链图片转存中…(img-eGucbUd7-1684216775707)]
联合
低位在前,小端存储
[外链图片转存中…(img-2jPM9Tbt-1684216775707)]
1234 —0x04D2