本文内容:
函数--》函数定义、子函数、函数作用域、主函数传参、递归函数
指针--》指针定义、指针当数组、函数传参指针、指针字符串操作、指针和数组、指针和函数、指针和const、野指针
1.函数的定义:”黑匣子“ 。通过一些特定的调用,去实现我们需要的功能
好处:代码模块化,查找错误方便
用法:主函数main() 子函数fun()
2.子函数编写组成
int add(int a,int b)
{
printf("%d
",a+b);
}
2.1 类型 int void char
返回值:整数 无返回值 字符
return
2.2 add 函数名
名字怎么取都行,但是要符合编程规范
2.3 {...}
函数功能具体
2.4 (int a,int b) 函数的形参
调用时候,传一个实参进去(类型匹配)
/*
1、通过输入数字,产生调用子函数,做一个计算器 + - x /
please input two number
12 23
please input computer (+ - * /)
+
秘籍:防止输入的时候产生多余的回车符,使用下%*c
*/
#include
int add(int a,int b)//做加法
{
printf("%d
",a+b);
}
int sub(int a,int b)//做减法
{
printf("%d
",a-b);
}
int mul(int a,int b)//做乘法
{
printf("%d
",a*b);
}
int div(int a,int b)//做除法
{
printf("%d
",a/b);
}
int main()
{
int num1,num2;
char bolo;
printf("请输入两个要计算的数:
");
scanf("%d",&num1);
scanf("%d",&num2);
printf("请输入您要计算的方式(+ - * /):
");
scanf("%*c%c",&bolo);
switch(bolo)
{
case '+':
add(num1,num2);
break;
case '-':
sub(num1,num2);
break;
case '*':
mul(num1,num2);
break;
case '/':
div(num1,num2);
break;
default:
printf("违规操作!
");
break;
}
return 0;
}
3.函数作用域
局部
一般来说在{}里面
全局
它在所有函数的外部定义,它的作用域是在整个.c里面的函数
如果全局变量和局部变量重名,那么全局变量的作用域将被抵消
extern 声明外部的变量
如果外部变量或者函数前面加上了static,就算你加个extern你没用
情况一:修饰函数,你想使用别人的.c
情况二:修饰全局变量
static 声明内部的变量, 别人没法使用的
声明全局变量,限制了全局变量的使用位置仅限当前.c
4.主函数传参
在函数运行时候,在./main后面可以添加参数
4.1 int main()
4.2 int main(void)
4.3 int main (int argc,char *argv[])
4.4 int main (int argc,char **argv)
4.5 int main (int argc,const char **argv)
1.argc====》传入参数的个数(main函数执行的时候参数的数量)
2.argv====》传入参数的值
gec@ubuntu:/mnt/hgfs/linux$ ./test alse return float
4 ./test
4 alse
4 return
作用:argc 对比输入参数是否有误
argv 对密码和对比用户
5.递归函数
解决某些核心问题:比如有一个问题,后面的步骤和前面的步骤存在相同的地方
阶乘 5! = 5*4*3*2*1
在递归的思想里面
自己调用自己,将n传参到函数里面,直到n=1时候
/*
2、编写一个程序, 此程序要求输入天数, 然后将该值转换为星期数和天数。
例如输入 18, 则要求输出:18 days are 2 weeks, 4days
*/
#include
void weekday(int date)
{
int week;
int day;
week=date/7;
day=date%7;
printf("%d days are %d weeks, %d days
",date,week,day);
}
int main(void)
{
int n;
printf("请输入天数:
");
scanf("%d",&n);
weekday(n);
return 0;
}
/*
3、编写一个程序, 用户输入某个大写字母,
产生一个金字塔图案,例如用户输入字母 E, 则产生如下图案:
A
ABA
ABCBA
ABCDCBA
*/
#include
int code(char a)
{
int i;
int j;
int s;
if( a>=65 && a<=90)//ASCII码中65-90都是大写字母
{
for(i=65;i<=a;i++) //行高
{
for(j=i;j<=a;j++)//空格
{
printf(" ");
}
int z=65;//字母初始
for(s=65;s<=2*(i-65)+65;s++)//字母
{
if(s
/*
4、编写一个程序, 该程序读取输入直到遇到#字符,
然后报告读取的空格数目、
读取的换行符数目
以及读取的所有其他字符数目。
*/
#include
int main()
{
int x=0;//空格
int y=0;//回车换行
int z=0;//其他
char code;
printf("please input a char:
");
while(1)
{
scanf("%c",&code);
if(code == '#')
{
break;
}
else if(code == ' ')
{
x++;
}
else if(code == '
')
{
y++;
}
else
{
z++;
}
}
printf("报告读取的空格数目 %d
",x);
printf("报告读取的换行数目 %d
",y);
printf("报告读取的其他数目 %d
",z);
}
C语言里面存放大量的相同类型的数据,无非就两种方法:
一:数组
二:指针
指针的定义: 存放地址的变量
定义方式:int *a;和int* a;这两个是相同的
这是一个int *类型的指针a
指针和数组关系:数组名是首地址
int a[10];
int *p;
p=a;
printf("%p
",a);
printf("%p
",&a[0]);
printf("%p
",p);
地址:
0xbfb08ad4
0xbfb08ad4
0xbfb08ad4
数组的访问:
第一种:通过下标直接去访问
第二种:通过指针去访问
指针的运算
解引用 *
直接在指针的前面加上*
int *p; <==>int* p;
指针:p ----地址
值 :*p ----具体的值
作用:获取指针的值
取地址
&
对象(变量的值)
作用:获取一个变量的地址
指针的大小
对于指针来说。在32位机器上,大小都是一样的,都是4字节
在64位机器上,大小都是一样的,都是8字节
定义int* char* 表示所指的类型
指针的加法
加法:
int *p;
printf("%p
",p);
p=p+1; //指针+1就是加上一个int类型
printf("%p
",p);
0xb777eff4
0xb777eff8 4字节
char *q;
printf("%p
",q);
q=q+1; //指针+1就是加上一个char类型
printf("%p
",q);
0xb76ccff4
0xb76ccff5 1字节
总结: 指针的加法:每次增加一个对应的类型
指针的减法:每次减少一个对应的类型
指针的操作
要注意的问题:
1.
int str[10];
int *p=str; //获取的首元素的地址
int *q=&str; //获取的是数组的地址
2.*号的优先级比+号要高的,
*p+1 先解引用p获取数值,然后数值+1
*(p+1) 先对指针加1,然后解引用获取数值
指针的应用1:将指针当做数组来用
char str[10]="helloworld";
char *p;
int i;
p=str;
for(i=0;i<10;i++)
{
printf("%c",p[i]);//指针带下标
printf("%c",*(p+i));
printf("%c",str[i]);
}
printf("
");
指针的应用2:函数传参指针
通过指针去修改变量的值
值传递:参数是普通的变量,传递的仅仅只是值,和原来的变量是没有关系
地址传递:参数是指针,可以通过指针修改值。
void *指针----》通用型指针 可以和其他的任何类型去匹配
指针的应用3:字符串操作
字符串
由一系列的字符组成
字符串的头文件:#include
比较常用的6个字符串:
1.strlen 计算字符串的长度
2.strcpy 复制一个字符串到另一个字符串
3.strcmp 比较两个字符串是否一样
4.strcat 拼接两个字符串
5.strstr 查找子串
6.strtok 分解字符串
查看资料:看看剩下的函数怎么使用
1.man手册
2.C语言标准函数库
3.http://www.baidu.com
strstr
头文件:#include
函数模型:char *strstr(const char *haystack, const char *needle);
函数作用:在一个字符串里面寻找子串
参数:const char *haystack 母串
const char *needle 子串
strtok
头文件:#include
函数模型:char *strtok(char *str, const char *delim);
函数作用:用来将一个字符串做拆分的
参数:char *str 要拆分的对象
const char *delim 拆分的标志
/*
1.使用指针操作,编写一个my_strcpy,将“hello”,拷贝到一个数组里面
*/
#include
#include
// 位置 字符串
void my_strcpy(char *a,char *b)
{
int i;
for(i=0;i
/*
2.通过指针操作:实现一个my_strcmp (strcmp()效果)
./main 123456
密码输入正确 打印hello
输入错误 程序结束
*/
#include
#include
int my_strcmp(char *s1,char *s2)
{
int i;
for(i=0;i
/*
3.将字符串“2018-08-07”中的年月日取出来
*/
#include
#include
int main(int argc,char **argv)
{
char str[]="2018-08-07";
char *delim="-";//分割标志
char *p;
printf("%s
",strtok(str,delim));
while(p=strtok(NULL,delim))
{
printf("%s
",p);
}
return 0;
}
指针的应用4:指针和数组
数组指针:-----》指向数组的指针
数组指针定义:
int (*p)[10];
int buf[10];
//p=buf;//指向数组的首元素 no
p=&buf;//指针指向整个数组
指针数组-----》数组里面存的是指针
指针数组定义:
int *p[10];<===>int* p[10];
常见例子
int main(int argc,char **argv)
char* *argv <===>char **argv
char* argv[]<===>char *argv[]
究竟如何去区分数组指针还是指针数组
看这个指针有没有加括号
总结:int buf[10];
1.buf 首元素的地址 等价于 &buf[0]
2.&buf 等价于一个数组指针
指针的应用5:指针和函数
1.数组作为函数的参数
实际上相当于一个指针,传递的地址
//int add(int buf1[5],int buf2[5],int buf[5])写法1
int add(int *buf1,int *buf2,int *buf)写法2
2.函数指针和指针函数
函数指针的写法
int (*p)(int a,int b);
int (*p)(int *a,int *b);
int add(int a,int b)
{
return a+b;
}
p=add;//p函数指针指向add函数
写法一:(*p)(12,23);
写法二:p(12,23);
指针函数的写法
int *p(int,int);
int *p(int a,int b);
int *p(int *a,int *b);
int add(int a,int b)
{
return a+b;
}
返回值:是一个指针
分析:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
分析第三个参数:void *(*start_routine) (void *)
第一步:用指针*p去替换*start_routine
第二步:void *(*p)(void *)
void (*p)(void *)
函数指针
void (*signal(int sig,void (*func)(int)))(int);
void ()(int);
分析:拆一个:void (*func)(int) 函数指针
*signal(int sig,int ) 指针函数 返回值:是一个指针
void (*p)(int); 函数指针
用变量 a 给出下面的定义
a) 一个整型数 int a;
b) 一个指向整型数的指针int *a;
c) 一个指向指针的的指针, 它指向的指针是指向一个整型int * *a;===int **a;
d) 一个有 10 个整型数的数组int a[10];
e) 一个有 10 个指针的数组, 该指针是指向一个整型数的int *a[10];
f) 一个指向有 10 个整型数数组的指针int (*a)[10];
g) 一个指向函数的指针, 该函数有一个整型参数并返回一个整型数int (*a)(int);
h) 一个有 10 个指针的数组, 该指针指向一个函数, 该函数有一个整型参数并返回一个整型数int (*a[10])(int);
指针用法6:cons
const是一个C语言的关键字,它限定一个变量不允许被改变
情况一:修饰指针,限定活动范围
写法一:
const int *p;
表示p可以访问指向地址中的内容,但是指针p不能修改指向的地址内容
写法二:
int const *p;
表示p可以访问指向地址中的内容,但是指针p不能修改指向的地址内容
写法三:
int *const p=&a;
表示p可以修改它指向的地址的内容,但是指针p不能够在指向其他的地址
情况二:修饰变量
1.const int a=10;
2.int const a=10;
3.const int a; no 常量只能在一开始就初始
a=10;
4.int const a; no
a=10;
指针用法7:野指针
你定义了指针,但是你没有指明内存地址,你就直接往里面写数据,----》段错误
讨论代码中所有的语句在内存中是如何存放?
栈内存:局部变量,空间是有限的,一般的操作系统8M左右,故不要在代码当中定义太大的局部变量(防止栈溢出)
堆内存:堆是一个自由内存,堆的大小不设限,它的大小由系统的物理内存决定
解决方案:
使用函数malloc calloc realloc 给指针分配一个堆空间,让它不再是野指针。
#include
void *malloc(size_t size);
参数:size_t size 你打算申请的空间字节数
返回值:你成功申请的那块内存的首地址
void free(void *ptr);释放空间
参数:void *ptr 释放你不需要的空间的首地址
void *calloc(size_t nmemb, size_t size);
按块来分配内存空间
参数:size_t nmemb 你打算申请多少块内存
size_t size 每一块内存空间的大小
void *realloc(void *ptr, size_t size);
给指针重新分配内存空间
参数:void *ptr 原来那块空间的首地址
size_t size 你想要重新申请多大的空间
区别:malloc 不会帮你将申请的空间清零
calloc 帮助你清零
realloc 重新分配的内存空间的首地址和原来的有可能一样,也有可能不一样