C语言总结

目录

 1 数据类型

1.1 常见数据类型

1.2 变量

1.2.1 概念:

 1.2.5   变量储存类型(萌新可以先跳过)

1.3 常量

1.3.2    字面常量:

1.3.3   const 修饰的常变量:

1.3.4    #define 定义的标识符常量:

1.3.5   枚举常量:

 2 运算符与表达式

2.1 赋值运算符与表达式

2.2 算术运算符与表达式

2.3 关系运算符与表达式

 2.4 逻辑运算符与表达式

2.5 逗号运算符与表达

2.6 条件运算符

2.7 sizeof运算符

2.8 运算符优先级与求值循序

3 语句的控制流

3.1 控制语句的类型

3.2 分支语句(选择语句)

3.2.1  if语句

3.2.2   switch语句

3.3 循环语句

3.3.1   while语句

3.3.2   do  while()

3.3.3for语句

3.4 转向语句

3.4.1   break语句

3.4.2   continue语句

3.4.3   goto语句

4 函数与程序结构

4.1 函数是什么

4.2 C语言函数的分类

4.2.1   库函数

4.2.2   如何使用库函数

4.3 自定义函数

4.3.1   自定义函数组成

4.4 函数的参数

4.4.1   实际参数(实参)

4.4.2   形式参数(行参)

4.5 函数的调用

4.5.1   传值调用

4.5.2   传址调用

4.6 函数的嵌套调用

4.7 函数的链式访问

4.8 函数的声明     

4.9 函数的定义

4.10 函数的递归

4.10.1   什么是递归

4.10.2   递归的两个必要条件

 5 数组

5.1 数组简介



 1 数据类型

1.1 常见数据类型

数据类型中文名称内存大小输入(printf)输出(scanf)
char字符型1byte%c%c
int整形4bytes%d%d
float单精度浮点型4bytes%f%f
double双精度浮点型8bytes%lf%lf

注~内存大小一般用sizeof运算符(并不是函数)计算来计算存放某一个量需要占用多少字节

例如:    printf("%d\n", sizeof(char));见2.7sizeof运算符

1.2 变量

1.2.1 概念

变量相当于内存中一个数据存储空间的表示,你可以把变量看做是一个房间的门牌号,通过门牌号我们可以找到房间,而通过变量名可以访问到变量(值)

1.2.2   变量定义方法:

                           1. 声明变量   int num;

                           2.赋值 num = 100;

                           3.使用 printf(“%d”,num);

                           4.熟练之后 int num = 100;

例如:

    int a = 10;      //整形
    char arr = 'A';   //字符型
    double i = 2.00;    //双精度
    float j = 3.0;     //单精度

1.2.3   变量的分类:

                   局部变量:

                   定义:在函数内部定义的变量。形式参数也属于。

                   作用域:只在定义它的函数内起作用。

                  全局变量:

                  定义:在函数外部定义的变量。

                  作用域:从定义变量的开始位置到源程序结束及(return 0;),全局变量可被所有函                    数共享。

例如:

int i = 0;                  //全局变量
int main()
{
    int j = 10;            //局部变量

    return 0;
}

注:当局部变量和全局变量同名的时候,局部变量优先使用。

1.2.4   变量初始化:

在main函数内部定义的变量称为局部变量,局部变量被定义后,系统不会对其初始化,你必须写程序对其初始化

int main()
{
     int i = 0;    //i初始化为0           
    return 0;
}

 1.2.5   变量储存类型(萌新可以先跳过)

1. 自动变量:

定义:储存在堆栈中

何为自动变量:被关键词auto修饰的局部变量为自动变量。(由于auto一般不使用,因为c语言默认局部变量为自动变量)

生命周期:一个函数开始执行,自动变量开始创建。当执行结束,自动变量被销毁。若再次执行该函数则重新创建。

int main()
{
    int j = 10;   //j为自动变量
    int i;
    auto  i = 0;   // i为自动变量
    return 0;
}

2.静态变量

定义: 储存在静态内存中,不属于堆栈中

何为静态变量:(1)局部变量为静态变量(2)被关键词static修饰的局部变量为静态变量

生命周期:在程序运行之前创建,在程序运行的期间一直存在,直到程序结束

#include <stdio.h>
int a = 0;          //静态变量,全局变量
void bianliang()
{
    static int j = 0;         //静态变量
    j++;
    int k = 0;              //自动变量,局部变量
    k++;
    printf("j=%d,k=%d\n", j, k);
}
int main()
{
    int i = 0;               //自动变量,局部变量
    for (i = 0; i < 3; i++)
    {
        bianliang();
    }
    return 0;
}


结果: j=1,k=1
      j=2,k=1
      j=3,k=1

3.寄存器变量

定义:顾名思义储存在在硬件寄存器中的变量。特点访问效率高

何为寄存器变量:(1)关键词register修饰的自动变量(2)只有自动变量才可以是寄存器变量(3)只限于int,char,指针类型变量使用

生命周期:本身就为自动变量,则函数变量在调用改函数时占用寄存器中存放的值,当函数结束的寄存器释放,变量消失

int main()
{
     register int i = 0;               
    return 0;
}

1.3 常量

1.3.1   概念:常量及常数,表示固定的值

常量分为以下以下几种:(1) 字面常量

                                        (2) const 修饰的常变量

                                        (3) #define 定义的标识符常量

                                        (4)枚举常量

1.3.2    字面常量:

直接写出的固定值

int main()
{
    6.6;           //整形不需要引号
    "a";
    'a';
    "hello";       //字符单双引号都可以
                   //字符串必须用双引号
    'hello';       //错误
    return 0;
}

1.3.3   const 修饰的常变量:

本质是变量,但具有常属性,所以不能被修改

int main()
{
    const int i = 0;
    i = 2;              //错误
    return 0;
}

1.3.4    #define 定义的标识符常量:

命令定义标识的常量,该标识常量在程序中是定值

#define CLINA 10    //修改10的值可以改变数组的个数
int main()
{
    int arr[CLINA] = { 0 };
    return 0;
}

1.3.5   枚举常量:

将一些用联系用意义的常量一起表达出来。用enum使用

int main()
{
    enum sex
    {
        男,
        C语言,
       学生,
    };

    printf("%d\n", 男);
    printf("%d\n",C语言);
    printf("%d\n", 学生);
    return 0;
}

 2 运算符与表达式

2.1 赋值运算符与表达式

运算符描述实例
=把右边操作值赋给左边a+b+c
+=把右边操作值加上左边操作值结果赋个左边

a+=b相当于a=a+b

-=把右边操作值减上左边操作值结果赋个左边a-=b相当于a=a-b
*=把右边操作值乘上左边操作值结果赋个左边a*=b相当于a=a*b
/=把右边操作值除上左边操作值结果赋个左边a/=b相当于a=a/b
%=把右边操作值模上左边操作值结果赋个左边a%=b相当于a=a%b

注:%模运算符,及取余数。a=6%5则余数为1所以a=1

赋值运算符支持char(字符型以ascll码值储存),int,double

字符串(字符数组)不支持赋值运算符

关于%的小知识;若要打印%则需要%%输入

int main()
{
    int a = 0;
    a = 6 % 5;
    printf("6%%5=%d", a);
    return 0;
}


结果:6%5=1

2.2 算术运算符与表达式

运算符描述实例(a为整型)
+两数相加

a=3+2

则a=5

-两数相减

a=3-2

则a=1

*两数相乘

a=3*2

则a=6

/两数相除

a=3/2

则a=1

%余数运算符,两数相除取余数

a=3%2

则a=1

++自增运算符,增1

a=3++

则a=4

--自减运算符,减1

a=3--

则a=2

注:为什么a=3/2则a=1,因为a为整型在c语言中整型不存在小数点后的值

int main()
{
    int a = 1.2;
    printf("%d\n", a);        //a=1

    int b = 0;
    b = 3 / 2;
    printf("%d\n", b);      //b=1

    double c = 0;
    c = 3 / 2;
    printf("%lf\n", c);      //c=1.000000

    float d = 0;
    d = 3 / 2;
    printf("%f\n", d);     //d=1.000000

    float f = 0;
    f = 3.0 / 2;
    printf("%f\n", f);         //f=1.500000  注意3,2为整型,想要的浮点型则在3或2或3,2加.0
    return 0;
}

2.3 关系运算符与表达式

关系数学中的表示C语言中表示
小于<<
大于>>
等于===
小于等于<=
大于等于>=
不等于!=

注意:(1)C语言中 = 的意思是赋值(2)==的意思是判断两个数是否相等   {关于这两个用法后面讲解}

(3)C语言中表示之间,之内,中间的表示方法

//表示12到18之间的数
    int i = 0;
    12 < i < 18;   //错误
    i > 12 && i < 18;  //正确,&&什么意思后面2.4讲解

 2.4 逻辑运算符与表达式

逻辑运算符C语言描述中文描述
&&逻辑与同时,并且
||逻辑或或者
逻辑非否定,相反

运算规则见3.2讲解。

逻辑表达式:(1)在C语言中真用1表示假用0表示。

                      (2)在表达式中非0的数字在逻辑运算时以1输出,表达式中0的数字在逻辑运算时以0输出

//以下均为逻辑表达式
    i = 12;       //在逻辑表达式中i=1
    i = 0;           //在逻辑表达式中i=0
    i = 12;       //在逻辑表达式中!i=0
    i > 12 && i < 16;  //在逻辑表达式中i=1
    i = 0 && i = 0;  //在逻辑表达式中i=0
    i = 12 || i = 16;  //在逻辑表达式中i=1
    i = -1 || i = 16;  //在逻辑表达式中i=1
    i =0 || i = 0;  //在逻辑表达式中i=0

(3)在逻辑与运算表达式中只有全为真才输出真否则为假

(4)在逻辑或运算表达式中若有一个或以上为真则输出为真否则为假

2.5 逗号运算符与表达

(1)逗号表达式,就是用逗号隔开的多个表达式。

(2)逗号表达式,从左向右依次执行。

(3)整个表达式的结果是最后一个表达式的结果。

 int a = 1;
    int b = 2;
    int c = ( a = b + 10, a, b = a + 1);   //逗号表达式
    printf("%d", c);  //c=13          //a=12,b=13 因为b在逗号多的最右边

2.6 条件运算符

条件运算符也叫三目运算符。由一个问号和一个冒号构成

1.语法:表达式1?表达式2:表达式3;

翻译:表达式1成立吗?如果成立只执行表达式2否则只执行表达式3

int i = 1;
    int j = 2;
    int c = (i > j) ? i : j;
    printf("%d", c);   //c的值为2

2.7 sizeof运算符

概念:sizeof是C语言的关键词,用来计算变量或数据类型的字节,sizeof不是函数


    int i;
    printf("%zd", sizeof(int));  //4  数据类型
    printf("%zd", sizeof(i));   //4   变量,两种写法
    printf("%zd", sizeof i);   //4

注意:(1)不能在子函数中对字符指针用sizeof

           (2)不能在子函数中对结构体指针用sizeof

2.8 运算符优先级与求值循序

优先级

运算符

名称或含义

使用形式

结合方向

说明

1[ ]数组下标数组名[常量表达式]左到右--
()圆括号(表达式)或函数名(形参表)--
.成员选择(对象)对象.成员名--
->成员选择(指针)对象指针->成员名--
2-负号运算符-表达式右到左单目运算符
~按位取反运算符~表达式
++自增运算符++变量名/变量名++
--自减运算符--变量名/变量名--
*取值运算符*指针变量
&取地址运算符&变量名
逻辑非运算符!表达式
(类型)强制类型转换(数据类型)表达式--
sizeof长度运算符sizeof(表达式)--
3/表达式/表达式左到右双目运算符
*表达式*表达式

%

余数(取模)

表达式%表达式
4+表达式+表达式左到右双目运算符
-表达式-表达式
5<<左移变量<<表达式左到右双目运算符
>>变量>>表达式
6>大于表达式>表达式左到右双目运算符
>=大于等于表达式>=表达式
<小于表达式<表达式
<=小于等于表达式<=表达式
7==等于表达式==表达式左到右双目运算符
!=不等于表达式!=表达式
8&按位于表达式&表达式左到右双目运算符
9^按位异或表达式^表达式左到右双目运算符
10|按位或表达式|表达式左到右双目运算符
11&&逻辑与表达式&&表达式左到右双目运算符
12||逻辑或表达式||表达式左到右双目运算符
13?:条件运算符表达式1?表达式2:表达式3右到左三目运算符
14=赋值运算符变量=表达式右到左--
/=除后赋值变量/=表达式--
*=乘后赋值变量*=表达式--
%=取模后赋值变量%=表达式--
+=加后赋值变量+=表达式--
-=减后赋值变量-=表达式--
<<=左移后赋值变量<<=表达式--
>>=右移后赋值变量>>=表达式--
&=按位于后赋值变量&=表达式--
^=按位异或后赋值变量^=表达式--
|=按位或后赋值变量|=表达式--
15,逗号表达式表达式1,表达式2....左到右--

注:次表用于用户embed_huang

3 语句的控制流

3.1 控制语句的类型

可分成以下三类: 1. 条件判断语句也叫分支语句:if语句、switch语句;

                              2. 循环执行语句: while语句、do  while语句、for语句;

                              3. 转向语句:break语句、continue语句、goto语句、return语句。

3.2 分支语句(选择语句)

3.2.1  if语句

语法结构:

if (表达式)
    {
        语句;
    }
    else
    {
        语句;
    }

    //多分支
    if (表达式)
    {
        语句;
    }
    if (表达式)
    {
        语句;
    }
    if (表达式)
        {
            语句;
        }
    else if (表达式)
    {
        语句;
    }
    else
    {
        语句;
    }

例:A级100~90,B级90~80,C级80~70,D级70~60,F级60~0,输入一个数字表达等级

#include <stdio.h>
int main()
{
    int j = 0;
    scanf("%d", &j);
    if (j <= 100 && j > 90)
    {
        printf("A级");
    }
    if (j <= 90 && j > 80)
    {
        printf("B级");
    }
    else if (j <= 80 && j > 70)
    {
        printf("C级");
    }
    if (j <= 70 && j > 60)
    {
        printf("D级");
    }
    if (j <= 60 && j > 0)
    {
        printf("F级");
    }
    else
    {
        printf("输入不正确");
    }
    return 0;
}

3.2.2   switch语句

语法结构:

switch (表达式)
    {
        case 常量表达式1:语句1;
        case 常量表达式2:语句2;
        case 常量表达式4:语句4;
        case 常量表达式4:语句4;
        default 语句n + 1;

    }

例:当输入1~5时候输出工作日,当输入6~7的时候输出休息日。

#include <stdio.h>

int main() {
	int week;
	printf("请输入星期时间对应的数字:");
	scanf("%d", &week);
	switch (week)
	{
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
		printf("工作日\n");
		break;
	case 6:
	case 7:
		printf("休息日\n");
		break;
	default:
		printf("输入有误!\n");
		break;
	}
	return 0;
}

注意:

(1)每段语句后面必须加break,当输入3时候判断case 1:不成立,然后判断case 2:不成立,然后判断case 3:成立进入语句执行case 4,执行case 5,执行printf输出工作日,然后break结束跳出switch。若没有break,继续执行case 6,执行case 7,执行printf输出休息日然后break结束跳出switch。

具体步骤看视频

switch中break作用-CSDN直播switch中break作用https://live.csdn.net/v/217827

(2)按照顺序执行

(3)case后面加一个空格

(4)default相当于没有相应de匹配值时候执行的语句,由于switch语句按照顺序执行所以放在最后

(5)switch语句中case后面的常量表达式一帮为int char 枚举常量(及整形常量),当使用char类型时候字符要加单引号例如‘A’,不能使用字符串因为字符符ascll码值储存

(6)可以多个case语句公用一个输出语句。及上面的星期一到星期五

3.3 循环语句

3.3.1   while语句

语法结构

while (表达式)
	{
		语句;
	}

例1:打印1~100所有整数

int main() 
{
	int i = 0;
	while (i<=100)
	{
		printf("%d ", i);
		i++;
	}
	return 0;
}


输出结果0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

例2:打印所有整数

int main() 
{
	
	int i = 0;
	while (1)
	{
		i++;
		printf("%d ", i);
		
	}
	printf("%d", i);

	return 0;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 ........

while语句的特点:

(1)执行的循序先执行表达式中的值,然后判断是否为0,为0直接跳过该while语句。不为0,则执行while循环语句中的语句执行完成后返回到表达式中继续判断是否为0,依此循环。如例1

(2)若表达式中的值一直不为0,那么一直执行下去,称作死循环。如例2,但是可以加入break,或者如例1中这样的判断进行控制。

3.3.2   do  while()

语法结构:

do
	{
		语句;

	} while (表达式);

do  while语句与while语句非常相似。do  while 语句每次执行一次循环都必须判断,而while则看具体代码。如while语句部分中例1需要每次循环结束进行判断,例2则不用只需要进行一次判断。

例1:输出1~50之间的整数

int i = 0;
	do
	{
		printf("%d", i);
		i++;

	} while (i<=50);

3.3.3for语句

语法结构:

for (表达式1; 表达式2; 表达式3)
	{
		语句;
	}

执行循序:进入for语句,执行表达式1(初始化步骤),只执行一次。     然后,执行表达式2(控制表达式,控制循环的终止),每一次循环都要进                    行判断。      然后执行for语句中的语句。     最后执行表达式3,一次循环结束。

重点:表达式1只开始的时候执行一次然后就不执行

           表达式2控制每次循环是否终止

           表达式3是每一次循环最后执行

例1:1~50之间的整数和

int i = 0;
	int sum = 0;
	for (i = 1; i <= 50; i++)
	{
		sum = sum + i;
	}
	printf("%d", sum);


输出结果:1275

3.4 转向语句

3.4.1   break语句

break语句一般for循环,while循环,do  while循环,switch选择。使该循环直接终止(结束)。

若是嵌套则结束整个嵌套程序。

int i = 0;
	while (1)
	{
		if (i == 3)
		{
			break;
		}
		printf("%d\n", i);
		i++;
	}
	printf("%d\n", i);



输出:0  1 2 3 注意:3是第二个printf输出

具体步骤看下面视频 

 break语句-CSDN直播break语句https://live.csdn.net/v/217829

3.4.2   continue语句

continue语句一般for循环,while循环,do  while循环,switch选择。使该位置到开始循环位置一直循环。

嵌套也一样

int i = 0;
	while (1)
	{
		if (i == 3)
		{
			continue;
		}
		printf("%d\n", i);
		i++;
	}
	printf("%d\n", i);

具体步骤看下面视频 

continue语句-CSDN直播continue语句https://live.csdn.net/v/217831

3.4.3   goto语句

语法结构:

	
	goto 常量1;

	goto 常量2;

    常量1:
	{
		语句1;
	}

	常量2:
	{
		语句2;
	}

	依此类推

结构:进入goto 常量1执行常量1:中的语句,然后进入goto 常量2执行常量2中的语句。

(个人感觉类似不switch语句)

注意,不能将位置倒置,否则进入死循环

4 函数与程序结构

4.1 函数是什么

数学中我们常见到函数的概念。但是你了解C语言中的函数吗?

维基百科中对函数的定义:子程序

在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method, subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组 成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。 一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。

4.2 C语言函数的分类

(1)库函数

(2)自定义函数

4.2.1   库函数

为什么会有库函数?

1. 我们知道在我们学习C语言编程的时候,总是在一个代码编写完成之后迫不及待的想知道结果,想 把这个结果打印到我们的屏幕上看看。这个时候我们会频繁的使用一个功能:将信息按照一定的格 式打印到屏幕上(printf)。

2. 在编程的过程中我们会频繁的做一些字符串的拷贝工作(strcpy)。

3. 在编程是我们也计算,总是会计算n的k次方这样的运算(pow)。

像上面我们描述的基础功能,它们不是业务性的代码。我们在开发的过程中每个程序员都可能用的到, 为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员 进行软件开发。

那怎么学习库函数呢?

这里我们简单的看看:www.cplusplus.com

简单总结一下简单的总结,C语言常用的库函数都有:

IO函数

字符串操作函数

字符操作函数

内存操作函数

时间/日期函数

数学函数

其他库函数

注意:库函数必须知道的一个秘密就是:使用库函数,必须包含 #include 对应的头文件。

4.2.2   如何使用库函数

需要全部记住吗?No

需要学会查询工具的使用: MSDN(Microsoft Developer Network)

www.cplusplus.com http://en.cppreference.com(英文版)

http://zh.cppreference.com(中文版)

英文很重要。最起码得看懂文献。

4.3 自定义函数

如果库函数能干所有的事情,那还要程序员干什么?

所有更加重要的是自定义函数。

自定义函数和库函数一样,有函数名,返回值类型和函数参数。

但是不一样的是这些都是我们自己来设计。

这给程序员一个很大的发挥空间。

4.3.1   自定义函数组成

ret_type fun_name(para1, * )
{
 statement;//语句项
}

其中:
ret_type 返回类型
fun_name 函数名
para1    函数参数

注意:

C语言中,定义一个函数的具体语法格式为:
返回值类型 函数名(参数类型 参数名1, 参数类型 参数名2, …, 参数类型 参数名n)
{
执行语句
……
return 返回值;
}
①返回值类型,用于限定函数返回值的数据类型(及return所返回的类型)。
②如果不声明返回值类型,那么默认返回int类型。
③当返回值类型为void时,return语句可以省略。(注:viod返回类型只在定义的函数返回,其他返回类型则返回到main()函数中并做相应的执行)

例1:比较两个数字的最大数

#include <stdio.h>
int get_max(int i, int j)
{
	return i > j ? i : j;
}
int main()
{
	int i = 10;
	int j = 20;
	int max = get_max(i, j);
	printf("max=%d\n", max);

	return 0;
}

4.4 函数的参数

4.4.1   实际参数(实参)

真实传给函数的参数,叫实参。

实参可以是:常量、变量、表达式、函数等。

无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形 参。

4.4.2   形式参数(行参)

形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内 存单 元),所以叫形式参数。

形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。

关于实参和行参见代码:

#include <stdio.h>
int get_max(int x1,int y1)      //x1,y1为行参

{
    return x1 > y1 ? x1 : y1;
}

int get_min(int* x2, int* y2)    //x2,y2为行参
{
    return *x2 < *y2 ? *x2 : *y2;
}

int main()
{
    int i = 1;
    int j = 2;
    int max = get_max(i,j);         //i,j为实参

    int min = get_min(&i, &j);     //&i,&j为实参

    printf("%d\n", max);

    printf("%d\n", min);

    return 0;

    
}

由上面代码得以下结论:这里可以看到 get-max 函数在调用的时候, x1 , y1 拥有自己的空间,同时拥有了和实参一模一样的内容。 所以我们可以简单的认为:形参实例化之后其实相当于实参的一份临时拷贝。

4.5 函数的调用

4.5.1   传值调用

函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。(如4.4.2代码中get_max则为传值调用)

4.5.2   传址调用

传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。

这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操 作函数外部的变量。(如4.4.2代码中get_min则为传址调用)

4.6 函数的嵌套调用

#include <stdio.h>
void get_printf2()
{
	printf("Hi\n");
}

void get_printf1()
{
	printf("Hellow\n");
	get_printf2();
}

int main()
{
	get_printf1();
	return 0;
}

注意:函数可以嵌套调用,但是不能嵌套定义        

4.7 函数的链式访问

#include <stdio.h>
int main()
{
    
    printf("%d ", printf("%d ", printf("%d ", 43)));
    //结果是啥?   4321
    //注:printf函数的返回值是打印在屏幕上字符的个数
    return 0;
}

及返回打印在屏幕上的字符个数
此处首先打印43,两个个数,返回2;则倒数第二个打印2,一个数字,返回1;第一个打印1

4.8 函数的声明     

 1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。

2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。

3. 函数的声明一般要放在头文件中的。

如:

#include <stdio.h>
void get_max(int i, int j)      //函数的声明
{
    

}

int main()
{
    int i = 10;
    int j = 20;
    get_max(i, j);
    return 0;
}

4.9 函数的定义

函数的定义是指函数的具体实现,交待函数的功能实现。

如:

#include <stdio.h>
int get_max(int i, int j)
{
    return i > j ? i : j;       //函数的定义
}
int main()
{
    int i = 10;
    int j = 20;
    get_max(i, j);
    return 0;
}

4.10 函数的递归

4.10.1   什么是递归

程序调用自身的编程技巧称为递归( recursion)。

递归做为一种算法在程序设计语言中广泛应用。

一个过程或函数在其定义或说明中有直接或间接调用自身的 一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。

递归策略 只需少量的程序就可描述出解题过程所需要的多次重复计算。

大大地减少了程序的代码量。

递归的主要思考方式在于:把大事化小

4.10.2   递归的两个必要条件

存在限制条件,当满足这个限制条件的时候,递归便不再继续。

每次递归调用之后越来越接近这个限制条件。

#include <stdio.h>
void print(int n)
{
	if (n > 9)
	{
		print(n / 10);
	}
	printf("%d ", n % 10);
}
int main()
{
	int num = 123;
	print(num);
	return 0;
}


输出:1 2 3 4

解析:

 5 数组

5.1 数组简介

5.1.1    一维数组的创建

数组是一组相同的类型的元素的集合。

数组的创建方式:

type_t   arr_name   [const_n];

//type_t 是指数组的元素类型

//const_n 是一个常量表达式,用来指定数组的大小

实例:

//代码1
	int arr1[10];
	//代码2
	char arr3[10];
	float arr4[1];
	double arr5[20];
	//代码3
	int count = 10;
	int arr2[count];   //错误

注:数组创建,在C99标准之前, [] 中要给一个常量才可以,不能使用变量。在C99标准支持了变长数组的概念。

5.1.2   一维数组的定义

数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。

实例:

#include <stdio.h>
int main()
{
	int arr1[3] = { 0 };

	int arr2[3] = { 1,2,3 };

	char arr3[3] = { 'a','b',1 };

	char arr5[] = { "Hi" };

	char arr6[4] = "Hi";
	printf("%s", arr5);     //输出Hi

	printf("%s", arr6);     //输出Hi

	return 0;

}

注意:(1)由于arr5,arr6为字符串则以%s代替

            (2)如果不想指定数组的元素个数就需要初始化数组的元素个数

            (3)若数组元素大于初始化个数则剩下的以\0代替

5.1.3   一维数组的使用

对于数组的使用我们之前介绍了一个操作符: [] ,下标引用操作符。它其实就数组访问的操作符。 我们来看代码:

例1

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };    //数组的不完全初始化
	int j = 0;

	for (j = 0; j < 10; j++)     
	{
		arr[j] = j;             //对数组的内容赋值,通过下标
	}

	for (j = 0; j < 10; j++)    
	{
		printf("%d ", arr[j]);      //对数组的下标访问,然后打印
	}

	return 0;

}

输出结果:0 1 2 3 4 5 6 7 8 9

例2 

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };     //数组的不完全初始化

	int j = sizeof(arr) / sizeof(arr[0]);    //    10/1=10
	printf("%d", j);
	return 0;

}

输出结果:10

总结:1. 数组是使用下标来访问的,下标是从0开始。(例1)

            2. 数组的大小可以通过计算得到。(例2)

5.1.4   一维数组在内存中的储存

#include <stdio.h>
int main()
{
    int arr[10] = { 0 };
    int i = 0;
    int j = sizeof(arr) / sizeof(arr[0]);     //求字符的个数

    for (i = 0; i < j; i++)
    {
        printf("&arr[%d] = %p\n", i, &arr[i]);
    }

    return 0;
}

结果:

&arr[0] = 0000009CC74FFC78
&arr[1] = 0000009CC74FFC7C
&arr[2] = 0000009CC74FFC80
&arr[3] = 0000009CC74FFC84
&arr[4] = 0000009CC74FFC88
&arr[5] = 0000009CC74FFC8C
&arr[6] = 0000009CC74FFC90
&arr[7] = 0000009CC74FFC94
&arr[8] = 0000009CC74FFC98
&arr[9] = 0000009CC74FFC9C

 5.1.5   二维数组的创建

//数组创建

int arr[3][4];

char arr[3][5];

double arr[2][4];

5.1.6   二维数组的初始化

#include <stdio.h>
int main()
{
    int arr1[3][4] = { 1,2,3 };
    int arr2[3][4] = {{ 1,2,3 }, { 4,5,6 }};
    int arr3[3][4] = { {1,2},{3,4},{5,6} };
    
    return 0;
}

int arr[][4] = {{2,3},{4,5}};

//二维数组如果有初始化,行可以省略,列不能省略 

arr1的空间图
arr[3][4]0123
01230
10000
20000

arr[3][4]代表3列,代表4行 

arr2空间图
arr[3][4]0123
01230
14560
20000
arr3空间图
arr[3][4]0123
01200
13400
25600

5.1.7二维数组的使用

#include <stdio.h>
int main()
{
	int arr[3][4] = { 0 };
	int i = 0;

	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			arr[i][j] = i * 4 + j;
		}
	}

	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("%d ", arr[i][j]);
		}
	}

	return 0;
}

5.1.8   二维数组的储存

像一维数组一样,这里我们尝试打印二维数组的每个元素

#include <stdio.h>
int main()
{
	int arr[3][4];
	int i = 0;

	for (i = 0; i < 3; i++)
	{
		int j = 0;

		for (j = 0; j < 4; j++)
		{
			printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
		}
	}
	return 0;
}

输出结果:

&arr[0][0] = 00000068BF0FF6A8
&arr[0][1] = 00000068BF0FF6AC
&arr[0][2] = 00000068BF0FF6B0
&arr[0][3] = 00000068BF0FF6B4
&arr[1][0] = 00000068BF0FF6B8
&arr[1][1] = 00000068BF0FF6BC
&arr[1][2] = 00000068BF0FF6C0
&arr[1][3] = 00000068BF0FF6C4
&arr[2][0] = 00000068BF0FF6C8
&arr[2][1] = 00000068BF0FF6CC
&arr[2][2] = 00000068BF0FF6D0
&arr[2][3] = 00000068BF0FF6D4

 5.2 数组的越界

数组的下标是有范围限制的。

数组的下标定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。

所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。

C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,

所以程序员写代码时,最好自己做越界的检查。

如:

#include <stdio.h>
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int i = 0;

    for (i = 0; i <= 10; i++)
    {
        printf("%d\n", arr[i]);    //当i等于10的时候,越界访问了
    }

    return 0;
}

二维数组的行和列也可能存在越界。

5.3 数组名

数组名及数组的首元素的地址

#include <stdio.h>
int main()
{
    int arr[10] = { 1,2,3,4,5 };
    printf("%p\n", arr);      //0000009F66F7FA98
    printf("%p\n", &arr[0]);  //0000009F66F7FA98
    printf("%d\n", *arr);     //1
    //输出结果
    return 0;
}

int arr[10] = {0};

printf("%d\n", sizeof(arr));               //40

 为什么输出的结果是:40?

补充:

1. sizeof(数组名),计算整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数组。

2. &数组名,取出的是数组的地址。&数组名,数组名表示整个数组。

3.除此1,2两种情况之外,所有的数组名都表示数组首元素的地址。

6 指针

6.1 指针与地址

6.1.1   指针概念

1.指针和C语言的其他变量一样,可以把指针看做专门储存地址的值。

2.平时口语中的指针通常指指针变量。

6.1.2   指针语法

1.创建指针变量

int* arr;
char* A;
double* a;

2.给指针变量赋值(先创建值后赋值指针)

int* arr;
char* A;
double* a;
 
int b = 1;      //创建值
char c = 'f';
double d = 1.1;
 
arr = &b;       //赋值
A = &c;
a = &d;

 3.取出变量保存地址的值

int i = 10;
int* arr = &i;
int b = *arr;

注意:变量和指针类型必须相同

6.2 指针与函数参数 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值