嗯,几年前学的,当时看的是郝斌09年的c语言视频,虽然他是用notepad写的,但是他讲的很详细,学起来很容易。那时候,我好像学C++刚结束😂😂,为了看C++和C的不同,以及准备考二级C,所以才去看的。当时用Word写了70多页笔记,现在想想,做笔记是耗费了我不少时间,不过还是学会了不少,尤其指针那里,我当时把什么字符、字符串、字符数组、字符指针一起比较,还把指针、指针函数,函数指针一起比较😂😂。好了不说了,让我来贴笔记吧。(格式下次调,我这用手机写的,不好调)
VC++6.0 软件测试
1 数据类型
整数
整型 - - int - -4
短整型 - - short (int) - -2
长整型 - - long (int) - -4
双长整型 – long long - -8
浮点数
单精度浮点数 - - float - - 4
双精度浮点数 - - double - - 8
字符
char - - 1
字符用单引号表示 ,字符串用双引号表示
例如:
‘A’ ‘b’ “A” ( A \0 ) “ABC”
2 什么是字节
字节就是存储数据的单位,并且是硬件所能访问的最小单位
默认进制为十进制
0(零)+数 为八进制
0X(零 字母X)为十六进制4
3 什么是ASCII
ASCII不是一个值,而是一种规定,规定了不同字符是使用哪个整数值去表示,它规定了
‘A’–65
‘B’–66
‘a’–97
‘b’–98
‘O’–48
字符的存储[字符本质上与整数的存储方式相同]
4 基本的输入和输出函数的用法
printf()—输出 :将变量的内容输出到显示器上
四种用法
printf(“<式样化字符串>”, <参量表>);
式样化字符串包括两部分内容:一部分是正常的字符(非控制输出符
),这些字符将按原样输出,另一部分是式样化规定字符(控制输出符
),以%开端,后跟一个或多个规定字符,用来确定输出的样式。
式样化字符串的样式
%[标志][输出最小宽度][精度][长度]式样字符
标志: 意义
-
: 结果左对齐,右边填空格
+
: 输出标记(正号或负号)
空格:输出值为正时输出空格,为负时输出负号
1、 prinf(“字符串”);
2、 printf(“输出控制符”,输出参数);
3、 printf(“输出控制符1 输出控制符2…”,输出参数1, 输出参数2);
输出的控制符和输出参数的个数必须一一对应
4、 printf("输出控制符 非输出控制符
", 输出参数);
输出控制符(有%的)包含如下:
%d - - int 以十进制输出
%ld - - long int
%c - - char
%f - - float
%lf - - double
%x (或者%X 后者 %#X) - - int 或 long int 或 short int 十六进制
%o - - 同上
%s - - 字符串
非输入控制符按原样输出
为什么需要输出控制符
01 组成的代码可以表示数据也可以表示指令
如果01组成的代码表示的是数据,那么同样的 01代码的组合以不同的输出格式输出
就会有不同的输出结果
例子1:
#include<stdio.h>
int main(void)
{
prinft("哈哈!\n"); //\n表示换行
int i = 10;
printf("%d\n", i); //d是十进制
int j = 3;
int k = 5;
printf("%d %d\n", j, k); //OK 3 5
printf("%d%d\n", j, k); // 35
printf("%d\n", j, k); //error 输出控制符和输出参数的个数不匹配
printf(“i = %d j = %d\n”, j, k); // i = 3 j = 5
return 0;
}
测试%x %X %#x %#X 的用法
例子2
#include <stdio.h>
int main (void)
{
int x = 47; //47 是十进制
printf("%x\n", x); // 输出结果:2f
printf("%X\n", x); // 输出结果:2F
printf("%#x\n", x); // 输出结果:0x2f
printf("%#X\n", x); // 输出结果:0X2F %#X 推荐使用
return 0;
}
5 scanf()的使用方法
【通过键盘将数据输入到变量中】
两种用法:
用法一:scanf (“输入控制符”, 输入参数);
功能: 将从键盘中输入的字符转化为输入控制符所规定格式的数据,然后存入以输入参数的值为地址的变量中
用法二: scanf(“非输入控制符 输入控制符”, 输入参数);
功能: 将从键盘输入的字符转化为输入控制符所规定格式的数据,然后存入以
输入参数的值为地址的变量中 。非输入控制符必须原样输入
如何使用scanf 编写出高质量代码
1、使用 scanf 之前最好先使用printf提示用户以什么样的方式来输入
2、scanf 中尽量不要使用非输入控制符,尤其是不要用\n
3、应该编写代码对用户的非法输入做适当的处理【非重点】
**
while ((ch=getchar())!=’\n’)
continue
**
例子1:
# include < stdio.h>
int main (void)
{
int i;
// i = 10;
scanf (“%d”, &i); //**&i 表示的i的地址 &是一个取地址符**
printf (“i = %d\n”, i); //i = 10
return 0;
}
运行结果:
123
i = 123
例子2:
#include <stdio.h>
int main (void)
{
int i;
scanf (“m%d”,&i); //m123是正确的输入,123是错误的输入
printf (“i = %d\n”, i);
return 0;
}
运行结果:
m123 m123n
i = 123 i = 123
例子3:
#include <stdio.h>
int main (void)
{
int i,j,k;
scanf (“%d %d”, &i, &j); // scanf (“%d,%d,%d”, &i, &j, &k);
printf (“i = %d, j =%d\n”, i, j);// printf (“i = %d, j =%d, k=%d\n”, i, j,k);
return 0;
}
运行结果:
1 2 1 2 3 1, 2, 3
i = 1 j = 2 i = 1, j = -858993460, k = - 858993460 i = 1, j = 2, k = 3
例子4
scanf 对用户非法输入的处理
include <stdio.h>
int main (void)
{
int i;
scanf(“%d”, &i);
printf(“i =%d\n”, i);
//....
while ((ch=getchar())!=’\n’)
continue
int j;
scanf (“%d”, &j);
printf(“j =%d\n”, j);
return 0;
}
运行结果:
123m //读m 的时候自动报错 123m
i = 123 i =123
j = -858993460 89
j = 89
例子6
include <stdio.h>
int main (void)
{
int i;
char ch;
scanf(“%d”, &i);
printf(“i =%d\n”, i);
scanf(“%c”, &ch);
printf(“ch =%c\n”, ch);
return 0;
}
运行结果:
12m 12nm
i = 12 i = 12
ch = m ch = n(因为char 占一个字节)
6 运算符
**算术运算符**
+ - * /(除) 取整 %(取余数)
除法/的运算结果和运算对象的数据类型有关,两个数都是int,则商就是int,若商有小数,则截取小数部分;被除数和除数中只要有一个或两个都是浮点型数据,则商也是浮点型,不截取小数部分。
16/5 = =3 16/5.0 = =3.20000 -13/4 = =-4 -13/-3 = =4 3/5 = =0
取余%的运算符对象必须是整数,结果是整除后的余数,其余数的符号与被除数(%左边的数)相同
13%3 = =1 13%-3 = =1 -13%3 = =-1
-13%23 = =-13 -13%-23 = =-13 3%5 = =3
规律:%中,若左边的数小于右边的数,则结果就为左边的数;若左边的数等于右边的数,则结果就为0;右边是1,结果为0
字符串数据和任何数据使用+都是相连接,最终都会变成字符串。
关系运算符
> >= < <= !=(不等于) = =(等于)
逻辑运算符
!(非) &&(并且) ||(或)
!真 假
!假 真
真&&真 真
真&&假 假
假&&真 假
假&&假 假
真||假 真
假||真 真
真||真 真
假||假 假
C语言对真假的处理
非零即真 零是假
真是1表示 ,假是0表示
&&左边的表达式为假,右边的表达式肯定不会执行 (不含有分号的是表达式, 含有分号的是语句)
||左边的表达式为真,右边的表达式肯定不会执行
赋值运算符
= += *= /= -=
优先级别:算数 > 关系 > 逻辑 > 赋值
三目运算符: ?:
使用方法:表达式?值1:值2
结果:表达式为真,则结果为值1,否则值为值2
7 流程控制语句
【是我们学习C语言的第一个重点】
什么是流程控制
程序代码执行的顺序
流程控制的分类 : 顺序和选择
定义:某些代码可能执行,也可能不执行,有选择性的执行某些代码
分类:
7.1 IF最简单的用法
include <stdio.h>
int main (void)
{
if (3 > 2)
printf(“AAAA\n”);
return 0;
}
1、if的范围问题
1.2、
由此可见: if默认只能控制一个语句执行或不执行,如果想控制多个语句的执行或不执行,就必须把这些语句用{}
括起来
2、if…else…的用法
include <stdio.h>
int main (void)
{
double delta = 3;
if (delta > 0)
printf(“有两个解!\n”);
/*{
printf(“有两个解!\n”);
printf(“哈哈\n”);
}*/
else if (delta == 0)
printf(“有一个唯一解\n”);
else
printf(“无解\n”);
return 0;
}
运行结果:
有两个解 有两个解
哈哈
4、C语言对真假的处理
非零即真,零就是假
真用1表示,假用0表示
5、if举例- -求分数的等级
例子1:
include <stdio.h>
int main (void)
{
float score ;
printf(“请输入您的成绩\n”);
scanf(“%f”,&score);
if (score > 100)
printf (“这是做梦\n”);
else if (score>=90 && score<=100)
printf(“优秀\n”);
else if (score>=80 && score<90)
printf(“良好\n”);
else if (score>=60 && score<80)
printf(“及格”):
else if (score>=0 && score<60)
printf(“不及格”);
else
printf(“输入的分数不合理”);
return 0;
}
例子2:
include <stdio.h>
int main (void)
{
int a, b, c;
int t;
printf(“请输入三个整数(中间以空格分隔)\n”);
scanf(“%d %d %d”, &a, &b, &c);
//编写代码完成a是中最大值,b是中间值, c 是最小值
if (a < b)
{
t = a;
a = b;
b = t;
}
if (a < c)
{
t = a;
a = c;
c = t;
}
if (b < c)
{
t = b;
b = c;
c = t;
}
return 0;
}
6、if的常见问题解析
7.2 switch 循环语句
(注意下方的常量表达式)
switch (表达式)
{
case 常量表达式1: 语句 1; //break;
case 常量表达式2: 语句2; //break;
. . . .
. . . .
. . . .
case 常量表达式n : 语句 n; //break;
default : 语句n+1;
}
没有break 语句使程序跳出swicth 语句 则程序将从选中的相应的语句开始,一直执行到右大括号处,若有break 语句,则每执行一条语句,都会跳出该循环。
If语句可以判断一个区间,执行效率比较低;switch 语句执行效率比较快,不可以进行区间判断;三目运算符 结构清晰不适于嵌套。
7.3 for循环语句
定义: 某些代码会被重复执行
分类
格式:
for (语句1; 条件语句;循环变量 )
语句A;
执行的流程【重点】
单个for 循环的使用
for (1; 2; 3)
语句A;
多个for 循环的使用
1、
for (1; 2; 3) //1
for (4; 5; 6) //2
A; //3
B; //4
整体是两个语句,1 2 3是第一个语句 4 是第二个语句
2、
for (1; 2; 3)
for (4; 5; 6)
{
A;
B;
}
// 加大括号后整体是一个语句
3 、
for (7; 8; 9)
for (1; 2; 3)
{
A;
B;
for (4; 5; 6)
C;
}
//加大括号后整体是一个语句
例子: 1 + 2 + 3 + …+ 100
#include <stdio.h>
int main (void);
{
int i;
int sum;
for(i=1; i<=100; ++i)
sum = sum + i;
printf(“sum = %d\n”, sum);
return 0;
}
强制类型转化
格式:
(数据类型) (表达式)
功能:
把表达式的值强制转化为前面所执行的数据类型
例子: (int)(4.5 + 2.2) 最终值是6
(float)(5) 最终值是 5.000000
浮点数的存储所带来的问题
float 和 double 都不能保证可以精确的存储一个小数
举例:
有一个浮点型变量 x(存储浮点数的近似值) ,如何判断 x 的值是否为零
if (|x -0.000001| < 0.000001)
是零
else
不是零
循环变量不能定义成浮点型
死循环是指:程序在执行循环时,没有循环出口(永真)
7.4 while 循环语句
while (表达式)
{
语句A;
循环体;
}
1、 只要表达式成立,语句就执行,否则跳出循环
2、与for 的相互比较
7.5 do …while 循环语句
1、 一般情况下,用 while 语句和 do …while 语句处理同一问题时,若两者的循环体部分是一样的,他们的结果也是一样的。 但是如果
while 后面的表达式一开始就为假(0值),则两种循环的结果是不相同的。
2、do …while 语句中while 后面的分号 ;是必不可少的
8 break 和 continue
8.1 break语句的格式:
break;
break语句**只能用在循环语句和switch语句
**(开关语句)中,
break语句在switch语句中的用法同上,在循环语句体中使用并被执行时,可以使程序终止循环
break出现在循环中,作用是跳出当前内循环语句,执行后面的代码。
break 语句对 if 语句不起作用
在多重循环中 一个break 语句只能向外跳出一层。
8.2 continue语句的格式
continue;
continue语句只能作用在循环语句的循环体中,作用是结束本次循环,即跳过循环体中剩余的语句,转到判断循环条件的位置,直接判断循环条件,决定是否重新开始下一次循环。直接结束本次循环,不影响下一次循环
。
continue 语句通常与if 语句一起使用,即满足某一条件时便终止该次循环。
9 数组
9.1 一维数组
怎样定义一维数组
为n个变量连续分配存储空间
所有的变量数据类型必须相同
所有变量所占的字节大小必须相等
数组下标都是从0开始
数组下标和第几个中的几相差一,下标比几小一
有关一维数组的操作
几种常见的错误的写法:
int a[5];
a[5] = {1, 2, 3, 4, 5};
只有在定义数组的同时才可以整体赋值,
其他情况下整体赋值都是错误的
int a[5] = {1, 2, 3, 4, 5};
a[5] = 100 // error 因为没有a[5]这个元素,最大的为a[4]
int a[5] = {1, 2, 3, 4, 5};
int b[5];
如果要把a数组中的值全部复制给b数组,错误的写法
b = a;//error
数组名不代表数组中所有元素。
数组名即为数组中第一个元素的地址,即为首地址
正确的写法
for(int i=0; i<5; i++)
b[i] = a[i];
例子:
#include <studio.h>
Int main(void)
{
int a[5];
return 0;
}
数据倒置
include <studio.h>
int main(void)
{
int a[7] = {1, 2, 3, 4, 5, 6, 7};
int i,j, t;
i = 0;
j = 6;
while(i < j)
{
t = a[i];
a[i] = a[j];
a[j] = t;
i++;
}
for(i=0; i<7; i++)
printf(“%d\n”, a[i]);
return 0;
}
9.2 二维数组
例子:
# include <stdio.h>
int main (void)
{
int a[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
}
for (int i=0; i<3; i++)
{
for (int j=0; j<4; j++)
{
printf(“%-5d”a[i][j]);//负表示左对齐,5表示5个空格
}
printf(“\n”)
}
return 0;
}
9.3 多维数组
函数【C语言的第二个重点】
为什么需要函数
避免了重复性操作
有利于程序的模块化
10 什么叫函数
逻辑上:能够完成特定功能的独立的的代码块
物理上:
能够接收数据【当然也可以不接收数据】
能够对接收的数据进行处理
能够将数据处理的结果返回
总结:函数是个工具,它是为了解决大量类似问题二设计的函数
include <stdio.h>
int f(void) //括号中void表示该函数不能接受数据,int 表示函数的返回值是int类型
{
return 10; //向主调函数返回10
}
int main(void)//
{
int j = 88;
j =f(); //j 的值又变成了10
printf(“%d\n”, j);
return 0;
10.1 如何定义函数
1、函数定义的本质是详细描述函数之所以能够实现某个特定功能的具体方法
2、return 表达式的含义:
1>终止被调函数,向主调函数返回表达式的值
2>如果表达式为空,则只**终止函数
**,不向被调函数返回任何值
3>break是用来终止循环和switch,return是用来终止函数的
例子:
void f()
{
return ;//return只用来终止函数,不向主调函数返回任何值
}
int f()
{
return 10; //第一:终止函数,第二:向主调函数返回10
}
3、函数返回值的类型也称为函数的类型,因为如果 函数名前的返回值类型和函数执行体中的return 表达式中表达式的类型不同的话,则最终函数返回值的类型以函数名前的返回值类型为准。【返回值的类型也就是该函数的类型】
例子:
include <stdio.h>
int f()
{
return 10.5;//返回的值为10,因为函数的类型为int类型
}
int void main(void)
{
int i = 99;
double x = 6.6;
x = f();
printf(“%lf\n”, x);
return 0;
}
例子:判断两个数的大小
# include <stdio.h>
int max(int i, int j)
//max 是函数的名字i和j是形式参数,简称 形参,void表示函数没有返回值 函数调用完毕后,为i , j分配的空间立即被释放。再次调用函数时,i和j分配的空间不一定跟上次调用分配的空间相同。
{
if(i>j)
printf(“%d\n”, i);
else
printf(“%d\n”, j);
}
int void main(void)
{
int a, b;
printf(“请分别输入两个数\n”);
scanf(“%d”, &a);
scanf(“%d”, &b);
printf(“最大的那个数为\n”);
max(a, b);
reurn 0;
}
程序从main函数开始,从main函数结束
函数的分类
有参函数 和 无参函数
有返回值函数 和 无返回值函数
库函数 和 用户自定义函数
值传递函数 和 地址传递函数 //有待进一步考虑
普通函数 和 主函数 (main函数)
一个程序必须有且只能有一个主函数
主函数可以是调用普通函数 普通函数不能调用主函数
普通函数之间可以相互调用
主函数是程序的入口,也是程序的出口
#include <stdio.h>
void max1(int i, int j)
{
if(i > j)
printf(“%d\n”, i);
else
printf(“%d\n”, j);
}
int max2(int i, int j)
{
if(i > j)
return i;
else
return j;
}
int main(void)
{
int a = 1, b = 2, c = 3, d = 9, e = -5, f = 100;
printf(“%d\n”, max2(a, b));
max1(a, b);
return 0;
}
判断一个数是否为素数
#include <stdio.h>
int main(void)
{
int val, i;
scanf(“%d”,&val);
for(i=2; i<val; i++)
{
if(val%i == 0)
break;
}
if(i == val) //说明val没有除尽2~val之间的任意一个数
printf(“是素数”);
else
printf(“不是素数”);
return 0;
}
使用函数:
#include <stdio.h>
bool IsPrime(int val)
{
int i;
for(i=2; i<val; i++)
{
if(val%i == 0)
break;
}
if(i == val) //说明val没有除尽2~val之间的任意一个数
return true;
else
return false;
//...一旦执行return 后面的均不再执行
}
int main(void)
{
int m;
scanf(“%d”,&m);
if(IsPrime(m))
{
printf(“是素数”);
else
printf(“不是素数”);
}
return 0;
}
浮点数的存储所带来的问题
Float 和double都不能保证可以把所有的实数都准确的保存在计算机中
因为浮点数无法准确存储,所以就衍生出来两个编程问题
例子
float I = 99.9;
printf(“%f\n”, i);
最终在Vc++6.0中的输出结果是:99.900002
if(|x-0.000001| <= 0.000001)
是零;
else
不是零;
函数的声明
#include <stdio.h>
void f(void); //函数声明,分号不能丢
int main(void)
{
f(); //函数的调用
return 0;
}
void f(void) //函数的定义
{
printf(“hello!\n”);
}
#include <stdio.h>
void f(void) //函数的声明加定义
{
printf(“hello!\n”);
}
int main(void)
{
f(); //函数的调用
return 0;
}
有函数声明时,函数声明必须放在函数调用的前面,函数的定义可放在函数调用的前面也可放在函数调用的后面;无函数声明时,函数的定义(声明)必须放在函数调用的前面
注意的问题
函数调用和函数定义的顺序
如果函数调用写在了函数定义的前面,则必须加函数前置声明
函数前置声明:
1、告诉编译器即将可能出现的若干个字母代表的是一个函数
2、告诉编译器即将可能出现的若干个字母所代表的函数的形参和返回值的具体情况。
3、函数声明是一个语句,末尾必须加分号
4、对库函数的声明是通过#include<库函数所在的文件的名字.h>来实现的
形参和实参
个数相同 位置一一对应 数据类型必须相互兼容
C语言常用的系统函数
double sqrt(double x); 求x的平方根
int abs(int x); 求x的绝对值
double fabs(double x) 求x的绝对值
10.2 变量的作用域和存储方式
全局变量
在所有函数外部定义的变量叫全局变量
全局变量使用范围:从定义位置开始到整个程序结束
局部变量
在一个函数内部定义的变量或者函数的形参,都统称为局部变量
void f(int i)
{
int j = 20;
}
i和j都属于局部变量
局部变量的使用范围:只能在本函数内部使用
注意的问题:
全局变量和局部变量命名冲突的问题
#include <stdio.h>
int i = 99;
void f(int i)
{
printf(“i = %d\n”, i);
}
int main(void)
{
f(8)://8
return 0;
}
在一个函数的内部如果定义的局部变量的名字和全局变量的名字一样=时,局部变量会屏蔽掉全局变量
按变量的存储方式
静态变量
自动变量
寄存器变量
11 指针 (地址)
#include <stdio.h>
int main(void)
{
int * p;// p是变量的名字,int * 表示P变量存放的是int 类型变量的地址
int i = 3;
p = &i; //ok 把i的地址赋值给p
p = i; //error p只能存放int类型变量的地址,不能存放int类型变量的值
p = 55; //error 原因同上
return 0;
}
p保存了i的地址,因此p指向i
p不是i,i也不是p
修改p的值不影响i的值,修改i的值也不会影响p的值
如果一个指针变量指向了某个普通变量,则*指针变量
就完全等同于普通变量
&:取地址符 用于取得一个可寻址数据在内存中的存储地址
*间接引用运算符,用于访问该指针所指向的内存数据
例子:如果P是个指针变量,并且P存放了普通变量的地址,则p指向了普通变量
*p就完全等同于 i
或者说:在所有出现*p
的地方都可以替换成i
在所有出现i
的地方都可以替换成*p
int *p
不表示定义了一个名字叫做*p
的变量
int *p
应该这样理解:p
是变量名,p变量的数据类型是int *
类型
所谓int *
类型实际就是存放int
变量地址的类型
int *p 声明了一个int类型的指针变量p,p指向了int类型的存储空间,p变量用来存储int类型的存储空间的地址
*p 取p所指向的存储空间中的内容 或 以p的内容为地址的变量
指针就是地址,地址就是指针
地址就是内存单元的编号
指针变量是存放地址的变量
指针和指针变量是两个不同概念
但是要注意,通常我们叙述时会把指针变量简称为指针,实际它们的含义并不一样指针本质就是一个操作受限的非负整数
指针不能访问未分配的内存空间的内容,换句话说,某个内存空间的地址要赋值给指针变量,*指针变量才能访问该内存空间里的内容(*指针变量要访问内存空间,内存空间的地址必须先赋值给指针变量)
指针常见错误
例子1:
#include <stdio.h>
{
int main(void)
int *p;
int i = 5;
*p = i;//报错 把i的值赋值给了一个没声明的空间 p没有指向已经分配的空间。
printf(“%d\n”, *p);
return 0;
}
例子2
include <stdio.h>
int main(void)
{
int i= 5;
int *p;
int *q;
p = &i;
//*q = p; //error *q 是int类型 而p是 int * 类型,用来存储地址
//其次 *q 没有指向已分配的int类型空间
//*q = *p //error *q 没有指向已分配的int类型空间
p = q;//q是垃圾值,q赋给p,p也变成垃圾值(随机值)
printf(“%d\n”,*q);
return 0;
}
例子3
#include <stdio.h>
void huhuan(int a, int b)
/*此a,b与下方的a,b属于不同的内存空间
由于函数调用完成后,此处分配的空间a,b就自动释放,所以主函数内的a,b值并没有互换成功(注意主函数的内存空间a,b并未释放)*/
{
int t;
t = a;
a = b;
b = t;
return;
}
int main(void)
{
int a = 3, b = 5;
huhuan(a, b);
printf(a = %d, b = %d\n, a, b); //结果: 3,5
return 0;
}
例子4
#include <stdio.h>
void huhuan(int &x, int &y)
/* &x和&y相当于给下方的a,b内存空间取了一个别名(小名字)该函数调用后,空间不释放,因为此处的内存空间与主函数分配的内存空间是同一个内存空间,即使此处的函数结束,主函数的内存空间并比释放*/
{
int t;
t = a;
a = b;
b = t;
return;
}
int main(void)
{
int a = 3, b = 5;
huhuan(a, b);
printf(a = %d, b = %d\n, a, b); //结果: 5,3
return 0;
}
例子5
#include <stdio.h>
void huhuan(int *p, int *q;)//a.b的地址分别赋值给了指针变量p,q
{
int t;
t = *p;
*p = *q;
*q = t ;
return;
/*
改变指针的指向
int *t;
t = p;
p = q;
q = t;
只改变了p,q指针的指向,并未改变a,b中的值
*/
}
int main(void)
{
int a = 3, b = 5;
huhuan(&a, &b);
printf(a = %d, b = %d\n, a, b); //结果: 5,3
return 0;
}
11.1 指针和一维数组:
数组属于构造数据类型
一维数组名是一个指针常量
,它存放的是一维数组第一个元素的地址
int a[10]
a 就为a[0] 的地址 ,a与&a[0]是等价的
int a[10]
int *p ;
p = a
(把数组首地址赋值给p, p和a均是指向数组空间) 等价于 p = &a[0]
下标和指针的关系
如果P是一个指针变量,则 p[i] 永远等价于*(p+i)
int *pArr,i =2;
pArr = &i;
* pArr = 3 仅相当于i = 3
*(pArr+i) 等价于 pArr[i] 因为数组a[5]的首地址赋值给了pArr
确定一个一维数组需要几个参数[如果一个函数要处理一个一维数组,则需要接受该数组的哪些信息]
需要两个参数:
数组第一个元素的地址
数组的长度
11.2 指针变量的运算符
指针变量不能相加 不能相乘 也不能相除
如果两个指针变量指向的是同一块连续空间中的不同存储单元,则这两个指针变量才可以相减
一个指针变量到底占几个字节
Sizeof(数据类型)
功能:返回值就是该数据类型所占的字节数
例子:sizeof(int) = 4 siezof(char) = 1
Sizeof(double) = 8
Sizeof(变量名)
功能:返回值就是该变量名所占的字节数
例子:
总结:
一个指针变量,无论它指向的变量占几个字节,该指针变量本身只占4个字节
一个变量的地址使用该变量首字节的地址来表示
11.3 动态内存分配
传统数组的缺点:
1、 数组长度必须事先制定,且只能是常整数或者是符号常量(defin或从const定义的常量),不能是变量
例子:
int a[5] ; //OK
int len = 5; int a[len]; //error
2、 传统形式定义的数组,该数组的内存,程序员无法手动释放
在一个函数运行期间,系统为该函数中数组所分配的空间会一直存在,直到该函数运行完毕时,数组的空间才会释放
3、 数组的长度一旦定义,其长度就不能再更改
数组的长度不能在函数运行的过程中动态的扩充或缩小
4、 传统方式定义的数组不能跨函数使用
A函数定义的数组,在A函数运行期间可以被其他函数使用,但A函数运行完毕之后,A函数中的数组将无法在被其他函数使用。
11.4 为什么需要动态分配内存:
动态数组很好的解决了传统数组的这4个缺陷
传统数组也叫静态数组
malloc 是memory (内存) allocate (分配)的缩写
p代表的就是一个int变量,只不过p这个整型变量的内存分配方式和11行的i变量的分配方式不同
free§ 表示把p所指向的内存给释放掉,p本身的内存是静态的,不是由程序员手动释放,p本身的内存只能在p变量所在的函数运行终止时由系统自动释放
11.4 动态内存分配—动态数组的构造:
假设动态构造一个int型一维数组
int *p = (int *) malloc (int len);
1、 本语句分配了两块内存,一块内存是动态分配的,总共len个字节,另一块是静态分配的,并且这块静态内存是p变量本身所占的内存,总共4个字节,
2、 malloc 只有一个int 型的形参,表示要求系统分配的字节数
3、 malloc函数的功能是请求系统len个字节的内存空间,如果请求分配成功,则返回第一个字节的地址,如果分配不成功,则返回null.
#include <stdio.h>
#include <malloc.h>
int main (void)
{
int a[5];//如果int占4个字节,则本数组总共包含有20 个字节,每四个字节被当做了一个int 变量来使用
int len;
int *pArr;
int i;
//动态的构造一维数组
printf("请输入元素个数:");
scanf("%d", &len);
pArr = (int *) malloc(4 * len); //第12行 本行动态的构造了一个一维数组,该一维数组的长度是len ,
//该数组的数组名是pArr,该数组的每个元素是int类型,类似于 int pArr[len];
//对一维数组进行操作 如:对动态一维数组进行赋值
for(i=0; i<len; i++)
scanf("%d", &pArr[i]);
//对一维数组进行输出
printf("一维数组的内容是:\n");
for(i=0; i<len; i++)
printf("%d\n", pArr[i]);
return 0;
}
11.5 静态内存和动态内存的比较:
静态内存是由系统自动分配,由系统自动释放
静态内存是在栈分配的
动态内存是由程序员手动分配的,手动释放
动态内存是在堆分配的
11.6 多级指针:
跨函数使用内存问题:
f()函数运行完成后,i的空间被释放,权限对属于操作系统,但指针却存储了i的地址并越权读出了i内存块的内容,所以逻辑上有问题
11.7 动态内存可以跨函数使用
#include <stdio.h>
#include<malloc.h>
void f(int **q)
{
*q = (int *)malloc(sizeof(int)); //sizeof(数据类型) 返回值是该数据类型所占的字节数
// 等价于p = (int *) malloc(sizeof(int));
// q = 5;
// *q = 5; //p =5
**q = 5; //*p = 5
}
int main(void)
{
int *p;
f(&p);
printf("%d\n", *p);
return 0;
}
12 结构体:
把一些基本类型数据组合在一起形成的一个新的数据类型,叫结构体
如何定义结构体:
3种方式,推荐使用第一种:
//第一种方式
这只是定义了一个新的数据类型,并没有定义变量
struct Student //定义Student数据类型
{
int age;
float score;
char sex;
};
Struct Student st1;
//第二种方式
struct Student2
{
int age;
float score;
char sex;
} st2; (st2是Student2类型的变量)
//第三种方式
struct
{
int age;
float score;
char sex;
} st3;
12.1 怎样使用结构体变量
赋值和初始化
定义的同时可以整体赋初值
如果定义完之后,则只能单个的赋初值
如何取出结构体变量中的每一个成员: (编译才有提示)
1、结构体变量名 . 成员名
2、指针变量 –> 成员名
(更常用)
指针变量 –> 成员名 在计算机内部会被转化成(*指针变量名).成员名的方式来执行,所以说两种方式是等价的
例子1:
例子2:
#include <stdio.h>
//第一种方式
struct Student //定义数据类型
{
int age;
float score;
char sex;
};
int main(void)
{
struct Student st = {80, 66.6, 'F'}; //初始化,定义的同时并赋值
struct Student * pst = &st; //&st不能改成st
st.age = 10;//第一种方式
pst ->age = 88; //第二种方式 等价于(*pst).age
return 0;
}
1、pst->age 在计算机内部会被转化成(*pst).age,所以pst->age等价于(*pst).age,也等价于st.age
2、我们之所以知道pst->age等价于st.age,是因为pst->age是被转化成了(*pst).age来执行
3、pst->age的含义;pst所指向的那个结构体变量中的age这个成员
例子3:
12.2 结构体变量和结构体变量指针作为函数参数传递的问题
推荐使用结构体指针变量作为函数参数来传递
# include<stdio.h>
#include <string.h>
struct Student
{
int age;
char sex;
char name [100];
};
void InputStudent1(struct Student *pst) //对结构体变量输入 函数1
{
pst->age = 10;
pst->sex = 'F';
strcpy(pst->name,"张三");
}
// 本函数无法修改主函数,该函数执行完,系统便释放空间
void InputStudent2(struct Student stu) //函数2
{
stu.age = 10;
strcpy(stu.name,"李四");
stu.sex = 'F';
}
void InputStudent3(struct Student &stu) //&取地址符,相当于给st取个别名 函数3
{
stu.age =20;
strcpy(stu.name, "王二");
stu.sex = 'M';
}
void OutputStudent(struct Student st) //对结构体变量输出
{
printf("%d %c %s\n", st.age,st.sex, st.name);
}
int main(void)
{
struct Student st;
printf("调用函数1:");
InputStudent1(&st);
OutputStudent(st);
printf("调用函数2:");
InputStudent2(st);
OutputStudent(st);
printf("调用函数3:");
InputStudent3(st);
OutputStudent(st);
return 0;
}
12.3 结构体变量的运算
结构体变量不能相加,不能相减,也不能互相乘除,但结构体变量可以相互赋值
举例
动态构造存放学生信息的结构体数组
动态构造一个数组,存放学生信息,然后按分数排序输出
链表:
13 计算机二级考试知识
默认为有符号数据类型signed
系统默认保留8位,该题设置保留2位
定义常量
定义变量 变量默认为auto类型
For循环例子:敲桌子
敲7 从1到100内的数,凡是7(个位带7或者十位带7)或7的倍数需要敲桌子
While循环例子:水仙花
水仙花数:从100到999之间 各个位数(个位 十位 百位)的立方和等于本身的数
for嵌套循环:
外层执行一次,内层执行一周
外层执行次数 * 内层执行次数 = 总执行次数
关于数组长度问题
支持,因为 SIZE是常量
不支持,因为size是变量
练习:
冒泡排序
在该条件判断中,如果是小于号就是降序,大于号就是升序
二维数组: 数据类型 数组名[行][列] 行 * 列 = 数组元素个数
行 :对应外层循环
列:对应内层循环
1 字符串与字符字符数组
字符数组和字符串之间的关系:
‘/0’结尾的char数组就是字符串,char数组空间个数 = 字符串长度 +1
字符数组输出利用循环输出,字符串可以利用循环输出,也可以利用字符串名输出
例子1
例子2
例子3
值传递,不改变实参,地址传递改变实参(用&或指针改变实参)
宏定义:无参数的宏定义
有参的宏定义:
//第一种定义
#define SUM(x,y) (x+y)
//第二种定义
#define SUM2(x,y)x+y //没有加括号
// #define SUM2(x,y)((x)+(y))
例子:
Int mian(void)
{ int a=10;
int b=20;
int sum;
sum=SUM(a,b) * 30; //sum=(10+20)*30;
sum=SUM2(a,b)*30; //sum=10+20*30;
printf(“%d\n”, sum);
return 0;
}
文件包含处理:
*p 是指:取p所指向的空间里的内容
Sizof(数据类型或变量名)
*指针作为函数参数:
*
数组名做函数参数
数组名做函数参数,函数的形参会退化为指针
9.4返回地址值的函数(指针型函数)
9.5指针数组,指向指针的指针
指针当做数组名操作
9.5.4指针数组(指针型数组,数组里存储的是地址)
Arr[2]取c的地址 *Arr[2]指c中的内容
如int a=3,*p;
p = &a ;
p = 5;//相当于a = 5,相当于(&a) = 5; 同上
9.4.5多级指针(考试不考)指向指针的指针,二级指针
typedef 是为已经存在的数据类型起别名而不是创建新类型
typedef 数据类型 别名;
10.2结构体类型数据的定义和成员的使用
结构体定义变量struct不可以省略
10.2.5结构体套结构体
数制和位运算
读文件:从文件中读取数据到控制台 写文件:将控制台数据写入到文件中
14 易混知识梳理
指针和字符串之间的关系:
char a ="abcdef";//错的 a是字符类型
char *q='a';//错的a不能转换为char*
char *p="abcdef";//对的 p指向字符串"abcdef"
cout<<p<<endl;//结果abcdef
例子:
#include<iostream.h>
struct Student
{
char* name;
int age;
}stu;
void setStudent()
{
stu.name="张三";
stu.age=23;
}
void getStudent()
{
cout<<"姓名:"<<stu.name<<endl;
cout<<"年龄:"<<stu.age<<endl;
}
void main()
{
setStudent();
getStudent();
}
反面例子:
char a[]={'a', 'b', 'c', 'd'};
char *q=a;
cout<<q;//错的,只有字符串才可以,字符串是特殊的字符数组,以’\0’结尾
最后我再唠嗑几句,当初学C,除了开头说的那两个原因之外,还有就是下学期学校开设了数据结构。
然而当时上学期,我只学了C++,而且是第一次接触编程语言,所以C++的指针那块没学太好,又加上楼主对自己专业不熟悉,那会也不知道哪里有学习的资源,也没有学长学姐推荐博客或者学习网站,所以C++的指针那块全靠看书看书看书!,但是依旧有点懵。
紧接着下学期就开设了数据结构,当时以为是C++写的,后来发现全是C语言写的,那时候楼主还没自学C,而且学过数据结构的都知道,数据结构超级重要
,而且涉及特别特别多的指针。刚好那会,我指针不会,所以楼主就捉急,就去百度找找办法,看见一个帖子里发了网盘链接,里面就是郝老师的C语言视频 (其实博主的网盘账户也是在那时候才注册的,也怪楼主大学前,压根没接触过这些东西,所以就被困在的一个圈子里,不知道外面的世界) ,链接里还有数据结构,当初真的把楼主高兴坏了,感觉自己“挖”到宝了!
博主找到了视频,后期也学了,但当把C学完时,数据结构那门课早就结束了,不过指针我最起码都能看懂了。
没办法,楼主当时加了两个社团,天天值班、签到忙成“狗”! 周六、周日有时还有一整天的值班! 哎呀,现在想想好扎心,大把的时光就这样没了!
总之,如果你学完了C,建议在学C++,或者直接学C++也行,C和C++差不多,就有一点不同,待会下边说。你学完C/C++了,强烈建议学好数据结构和算法,然后再谈论其他语言、设计模式之类的。别的我不知道,但我一直认为**,数据结构是一门很重要很重要的学科!
**
好了,唠嗑完了,下边来说一下C和C++的区别:
首先,C是面向过程的语言,而C++是面向对象的。
不过我觉得C++差不多就是在 C的基础上多了面向对象,外加一些其他的不同,毕竟语言发展要向前,总不能向后吧!哈哈哈~
其次,C头文件和C++的头文件不一样:
来看看C:
#include <stdio.h>
int main (void)
//代码省略
return 0;
}
再来看看C++:
#include<iostream.h>
void main()
{
//代码省略
}
当然,他们对应的还有其他的写法,比如头文件里的h
不写,然后使用namespace什么的,我不常用,忘记了,别问我,问我,就是“聂导三连
”
然后,C与C++的输入输出不一样
来看看C:
输入:
//用法一:
scanf ("输入控制符", 输入参数);
/*功能: 将从键盘中输入的字符转化为输入控制符所规定格式的数据,然后存入以输入参数的值为地址的变量中*/
//用法二:
scanf("非输入控制符 输入控制符", 输入参数);
/*功能: 将从键盘输入的字符转化为输入控制符所规定格式的数据,然后存入以
输入参数的值为地址的变量中 。非输入控制符必须原样输入 */
输出:
prinf("字符串");
printf("输出控制符",输出参数);
printf("输出控制符1 输出控制符2......",输出参数1, 输出参数2);
// 输出的控制符和输出参数的个数必须一一对应
printf("输出控制符 **`非输出控制符`**", 输出参数);
再来看看C++
输入:
cin>>输入参数;
输出:
cout<<输出参数;
接着,C与C++动态分配内存不一样
来看看C:
#include<stdio.h>
#include<malloc.h> //不能省略
int main(void)
{
//需要使用malloc函数来分配内存
int* p = (int *)malloc(sizeof(int)); //sizeof(int) 返回的是int所占的字节数
*p = 4;
int* q=p;
printf("%d\n",*q);
}
运行结果:
再来看看C++;
#include<iostream.h>
void main()
{
int* p = new int;
*p = 8;
int* q = p;
cout<<*q<<endl;
}
运行结果:
嗯,总结就这些了,当初就了解这些,如有不足之处,还望各位大佬给予纠正!
更多推荐
C语言自学笔记
发布评论