c语言笔记
第一个程序(梦开始的地方)
#include<stdio.h>//头文件
int main()
{
printf("hello word");//打印hello word
}
//变量 一个存储数据的;
//定义变量 变量类型 变量名;
//作用域 变量的作用域在把它包含在{}里面最近的;
//生命周期 重调用时就是生命开始到结束;
//五则运算 + - * / % 前面4个和我们学的一样,后面的%是取模也叫取余;
//单目运算 ++ – 使变量加1或者减1 注:i++ 是先用后加,而++i是先加后用;
作业:查找一下**%c %s %o %x %f %p %u** //百分号什么是控制我们输出的格式
//& :取地址符 把变量的内存地址取出来
// :解地址符 把变量的内存地址里面存的东西解出来*
%c:输出字符
%s :输出字符串
%o:输出无符号以八进制表示整数
%x:输出无符号以十六进制表示的整数
%f: 输出浮点数
%p: 输出指针值
%u:十进制无符号整
进制转换
数据存储是以字节(byte)为单位
数据传输是以位(bit)为单位 //一字节等于八
小白用法欸
10进制转16,先转二进制,再隔4个转16,比如 97=0110 0001=61
10进制转8,先转二进制,再隔3个转8进制,比如97= 001=1 100=4 001=1
10进制转2,10进制除以2,
比如
97/2=48....1
48/2=24....0
24/2=12....0
12/2=6.....0
6/2=3......0
3/2=1......1一直算到1然后把最后的1也加上从后先前记110 0001。
正经的用法
10进制转16
97/16=97-(16*6)=1(这边加上16乘以的数就好,再加上1,就是61。
10进制转8
97/8=97-(8*12)=1
12/8=12-8=4 (算到比8小的就可以不用算了,加上一就好,141).
数据类型
基本数据类型
int //整型 //4字节
char //字符型 //1字节
short //短整型 //2字节
long //长整型 //4字节
float //单精度浮点数 //4字节
double //双精度浮点数 //8字节
注:以上是在32位的系统下的字节
在计算机里是用补码+1表示负数
要得到补码先取反,比如1,二进制就是0000 0001,取反1111 1110,加1,1111 1111,这就是补码;
逻辑判断
&& //逻辑与 必须2个都是真才真,否则为假
|| //逻辑或 有一个为真就是真,2个假为假
! //非 真的变假的,假的变真的 非零的数和零,真和假,不管什么数只要不是0,就是真,是零就是假
!= //不等于 不相等
== //等于 等于
位运算
& //按位与 必须2个都是1才是1,否则为0
| //按位或 有一个为1就是,就为1
~ //按位取反 ~1111 等于0000 若n为任何一个十进制的数则:“~n+n=-1” ~1+1=-2
^ //异或 相同为0,不同为1
>> /*右移 二进制向右移动,比如a=4=0100,a>>1(让a向右移动一位),然后a=2=0010 对于无符号的int和char型数据,右移时左端补零,对于有符号int和char型数据,空出来补符号位
比如-3 是先3取反1100(原码0011),然后加上1变成补码1101,这就是-3,然后正常来说位移是0110,但是它是有符号的所以要加上前面加上1,也加上1110,然后就位移成了-1(这是通俗易懂的)。
然后就是正常来说int 是4字节,每个字节8位,也就是3的源码是00000000 00000000 00000000 00000011
那取反后就是11111111 11111111 11111111 11111100,
补码是11111111 11111111 11111111 11111101
然后位移>>就是01111111 11111111 11111111 11111110,
但是要补上符号位所以就是11111111 11111111 11111111 11111111,也就是-1.
*/
<< //左移 二进制向左移动,比如a=4=0100,a<<1(让a向左移动一位),然后a=8=1000
x|=(1<<y) //将x的第y位置为1
x&=~(1<<y) //将x的第y位清零
内存图
在32位的操作系统,为我们分配了4g的内存 32g=2^32
指针是一个特殊的变量,它存储的是内存里的地址.
在32位的系统,无论是什么指针都是4字节(sizeof()可以用尝试一下),因为指针本身只有4字节
在定义指针的时候“*”只有在定义指针的时候才表示是指针,其他任何时都不表示是指针。
n级指针用来存储n-1的指针,比如
int a=10;
*p=&a;
**p1=&p;(一级指针解一次,二级解两次);
大小端
在小端是高地址存高位,低地址存低位,大端是高地址存低位,低地址存高位。
程序结构
顺序结构(从上往下顺序执行的程序)
#include<stdio.h>
int main()
{
int i=5;
int j=4;
int sum=i+j;
}
分支结构
if分支
#include<stdio.h>
int main()
{
int i=5;
int j=4;
int sum=i+j;
if(i>5)
{
printf("hello ");
}
else //else 是否则,有if可以没有else,但是else 前面必须有if
{
printf("word");
}
}
/********************************************************************************************************************************/
#include<stdio.h>
int main()
{
int i=5;
int j=4;
int sum=i+j;
if(i>5)
{
printf("hello ");
}
else if() //else if 是多一个判断,也可以不加,下面的else{}就只判断if
{
printf("word");
}
}
/********************************************************************************************************************************/
#include<stdio.h>//这是if嵌套
int main()
{
int i=92;
int j=3;
if(i>=90)
{
printf("KFC\n ");
if(j<=5)
{
printf("200\n");
}
}
else
{
printf("NULL\n");
}
}
switch()分支
switch()//填写变量
{
case 1:
printf("hello\n");
break;
case 2:
printf("hello123\n");
break; //break这是结束,在结束离它最近的{}
default: //这也是结束,是标记switch的末尾。
printf("hh");
}
三目运算
条件 ? 结果1:结果2
#include<stdio.h>//这是if嵌套
int main()
{
int i=5,j=9;
printf("%d",(i>j)?i:j);
}
循环结构
for循环
int sum;
for(int i=0(初始化条件);i=<10(退出条件);i++(循环控制条件)) //i可以在for前面定义初始化,但是不能在for后面,
{ //循环体
sum+=i;
}
while循环
while() //逻辑判断,为真就循环,为假退出,非零死循环,零是不运行
{ //循环体
}
do…while循环
do //先运行后判断,只是少运行一次
{ //循环体
}while; //do while最后必须加;
go to循环
xuedao: //标签名加:
int i=1;
if(i<10) //条件用if跳转
{
i++;
}
else
{
goto exit
}
exit:
return 0; //返回值
循环控制
break; //跳出整个循环
continue; //跳出单次循环
循环嵌套
for(int i=1;i=<9;i++) //九九乘法表就是基础嵌套;
{
for(int j=1;j=<i;j++)
printf("%d*%d=%d\t",j,i,i*j);
printf("\n");
}
数组
一维数组
一维数组(可以看出excel的一行或一列表格)
数组就是相同数据的数据集合
数组名代表首地址
内存是从上往下分配的,但是数组是从下至上的
//可以把一维数组想象成一行,或者一列的表格
#include<stdio.h>
//数据类型 数组名[元素个数]
int main()
{
int a[10];
printf("%d",*a);//获取首地址
printf("%d",*(a+1))//获取首地址的下一个元素的地址
}
//sizeof计算内存大小,可以用来算数组大小
二维数组
二维数组可以看成一个面(也可以看出excel的整个表)
#include<stdio.h>
int main()
{
int a[2][3];
for(int i=0;i<2;i++)
{
for(int j=0;j<3;j++)
{
printf("%d",a[i][j])//变量二维数组
printf("%d",*(*a+0) ) //获取二维数组的第0行第0个数
printf("%d",*(*(a+0)+0) ) //获取二维数组的第0行第0个数
printf("%d ",*(*(a+2)+1)); //第3行第2列 二维数组需要解两次才能用
printf("%d",*(*(a+i)+j) )//获取二维数组的第j行第i个数。
}
}
}
指针
指针是变量,地址是常量
#include <stdio.h>
//
int main()
{
int i = 8;
int *p =&i;
int **p1 = &p;
printf("&i = %p\n",&i);//变量i的地址
printf("&p = %p\n",&p);//变量本身p的地址
printf("*p = %p\n",*p);//*p == *(&i)
printf("*p1 = %p\n",*p1);//*p1 == &i !=&p
printf("**p1 = %p\n",**P1);//8
}
#include<stdio.h>
#include<stdlio.h>
int main()
{
int *p=malloc(sizeof(int)*4);//向系统要内存空间malloc(大小),它返回的是指针;
*p=10;
*p=20;
free(p);//释放内存
p=NULL;//必须置空,预防野指针;
}
int *p;//就是int类型
指针的类型决定了访问内存大小,和指针+1加多少
char *p[];//在只读区,不能修改而且要取首地址要解2次才可以
数组指针
int brr[2][3];
int (*p)[3]; //只有多维数组才可以用数组指针
解开和解数组是一样的
printf("%d",*(*(a+i)+j) )
查找
#include<stdio.h>//二分法查找(必须是有序)
int sea(int a[] ,int len,int n)//传入数组,数组长度,查找的位置
{
int left= 0; //左边
int right= len-1; //右边
while (left<=right) //当左变小于等于右边时结束,就是说前面的加到超过了right就结束,代表没有这个数
{
int middle =left+((right-left)/2);//用必定最大的左边减右边,除以2,
if(a[middle]>n) //判断中间的是否大于n
{
right=middle-1; //缩小范围
}else if(a[middle]<n) //判断中间的值是否小于n
{
left=middle+1; //缩小范围
}else{
return middle; //查找到后,返回中间值
}
}
return -1; //返回值为-1
}
int main()
{
int len=15,n=5;
int a[15];
for(int i=1;i<1;i++)
{
a[i]=i;
}
if(sea(a,len,n)==-1)
{
printf("NULL");
} else
{
printf("下标是%d",sea(a,len,n));
}
}
排序
for (int i = 0; i <len; i++)//冒泡
{
for(int j=i+1;j<len;j++)
{
if(a[i]<a[j])
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
}
for (int i = 0; i <len; i++)//选择
{
mid=i;
for(int j=i+1;j<len;j++)
{
if(a[mid]>a[j])
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
}
void cr(int arr[],int len)
{
int key;
for(int i=1;i<len;i++)
{
key=arr[i];
j=i-1;
while((j>=0) && (arr[j]>key))
{
arr[j+1]=arr[j];
j--;
}
arr[j+1]=key;
}
}
字符、字符串、字符数组
字符‘a’ ‘b’ ‘\n’
字符串 “hh" “hello”
字符串默认以‘\0’结尾
%s是输出一个字符串,给它一个地址,然后解开地址,并打印输出地址里面的值
假如有指针指向了一个字符串,用%s的时候就,不需要解地址
指针可以直接存储字符串,但是指针的存储的字符串在只读区域,不能修改;
可以用循环输出指针存的字符串,
#include<stdio.h>
int main()
{
while(*p)
{
printf("%s",p);
*p++;
}
}
内存分段
堆内存(heap),栈内存(stack)
数据段分为 未初始化的全局变量(bss)、全局变量(global)、静态变量区(static)、只读区(readonly)
代码段(code)
//程序分为5个阶段编辑、预处理、编译、链接、执行
//预处理把原代码分为语法单元,去掉注释,同时把#define与#include等预处理指令定义和包含的内容展开;
//编译器检测分析源代码的语法单元,检测源代码的有效性,检查成功则产出对应的机器码,符号
//链接器分析对外部变量和函数引用,找到符号(有且唯一),确定数据段与代码段地址,并产生可执行程序,在一个程序中,一定不能重复定义外部变量和函数
函数
//封装调用代码
//函数的作用是将代码模块化
#include<stdio.h>
//函数是由返回值 函数名 参数列表;
//传参 值传递 地址传递
int test(int a,int * b)//子函数,int是返回值类型、函数是test、int是参数列表 如果前面的int 是void是返回值为空,也就是没有返回值,
// 返回值类型 函数名(参数列表)
{
a++; //值转递不改变主函数的变量
(*b)++; //地址传递改变了主函数的变量
printf("%d" ,a);
printf("%d" ,b);
return a,b; //返回值必须和函数名前面的返回值类型一致,返回值返回了直接结束当前函数
}
int main()//主函数
{
int a=5,b=5;
test(a,&b); //调用函数,a是值传递,而b是地址传递给函数
}