您可以通过左右滑屏查看更多笔记内容。。
编写一个计算平均值的小程序;
mkdir lens6-->vi avg.c
#include <stdio.h>
int main(){
//求平均值;
int s,n;
scanf("%d,%d",&s,&n);
float v=s/n;
printf("v=%f\n",v);
return 0;
}
wq-->gcc avg.c -o avg.out-->./avg.out(测试可用)
编写一个计算用户输入数据的个数与和的小程序;
#include <stdio.h>
int main(){
int flag=1;
int i;
//计算用户输入的数据个数;
int count=0;
//计算所有数据的和;
int s=0;
while(flag){
scanf("%d",&i);
if(0==i) break;
count++;
s+=i;
}
printf("%d,%d\n",s,count);
return 0;
}
wq-->cc input.c -o input.out-->./input.out(测试可用)
结合以上两个小程序,实现,输入n 个数据,知己计算n 个数据的平均值;
./input.out | ./avg.out
100 ,100,100,100,100
v=100.000000
#### “ * ”和”&” 区别
**” * “:指针 “&” 引用 **
概念上讲。指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。 而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量)。
★相同点: ●都是地址的概念; 指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。
★不同点: ●指针是一个实体,而引用仅是个别名; ●引用只能在定义时被初始化一次,之后不可变;指针可变;引用“从一而终”,指针可 以“见异思迁”; ●引用没有const,指针有const,const的指针不可变; ●引用不能为空,指针可以为空; ●“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小; ●指针和引用的自增(++)运算意义不一样; ●引用是类型安全的,而指针不是 (引用比指针多了类型检查)
指针:
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元;
#include <stdio.h>
//在a,b前加*
void change(int *a,int *b){
int tmp=*a;
*a=*b;
*b=tmp;
}
int main(){
int a=3;
int b=5;
//在a,b钱加&
change(&a,&b);
printf("num a=%d\nnum b=%d\n",a,b);
}
一、gdb:
UNIX及UNIX-like下的调试工具:
功能:一般来说,GDB主要帮助你完成下面四个方面的功能:
1、启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
2、可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
3、当程序被停住时,可以检查此时你的程序中所发生的事。
4、你可以改变你的程序,将一个BUG产生的影响修正从而测试其他BUG。
二、gdb常用指令:
gcc -g main.c //在目标文件加入源代码的信息, -g //-g后可调试文件 gdb a.out (gdb) start //开始调试 (gdb) n //一条一条执行 (gdb) step/s //执行下一条,如果函数进入函数 (gdb) backtrace/bt //查看函数调用栈帧(查看函数堆栈) (gdb) info/i locals //查看当前栈帧局部变量 (gdb) frame/f //选择栈帧,再查看局部变量 (gdb) print/p //打印变量的值 (gdb) finish //运行到当前函数返回 (gdb) set var sum=0 //修改变量值 (gdb) list/l 行号或函数名 //列出源码 (gdb) display/undisplay sum //每次停下显示变量的值/取消跟踪 (gdb) break/b 行号或函数名 //设置断点 (gdb) continue/c //连续运行 (gdb) info/i breakpoints //查看已经设置的断点 (gdb) delete breakpoints 2 //删除某个断点 (gdb) disable/enable breakpoints 3 //禁用/启用某个断点 (gdb) break 9 if sum != 0 //满足条件才激活断点 (gdb) run/r //重新从程序开头连续执行 (gdb) watch input[4] //设置观察点 (gdb) info/i watceping/248chpoints //查看设置的观察点 (gdb) x/7b input //打印存储器内容,b--每个字节一组,7--7组ceping/248ceping/248 (gdb) disassemble ceping/248 //反汇编当前函数或指定函数 (gdb) si // 一条指令一条指令调试 而 s 是一行一行代码 (gdb) info registers // 显示所有寄存器的当前值 (gdb) x/20 $esp //查看内存中开始的20个数 (gdb) q //退出
a,b在两个函数中是局部变量。 ceping/248
通过gdb调试ceping/248案例可以查看*a 的值,是地址;
查看未加*时的方法
(gdb) s
change (a=3, b=5) at main2.c:4
4 int tmp=a;
查看加*时的方法
(gdb) s
change (a=0x7fffffffdeb0, b=0x7fffffffdeb4) at main.c:4
4 int tmp=*a;
计算机中数据表示方式:二进制;(满二进一,只有0 1);
指针:本质就是地址:
变量:本质就是内存;
&是取地址运算符
*pa=&a与pa=&a之间的疑惑, *pa=&a 是把a的地址值 给指针pa里的数据赋值. pa=&a是把a的地址值赋值给pa,使得指针pa(指向)a的地址 可以理解为 *pa=&a,是“值
int *pa是指定义一个指向int类型的指针pa。&在这里是取址的意思&a就是取a的地址。所以int *pa=&a;意思是定义一个指向变量a的指针pa
例如:(难点)
#include <stdio.h>
int global=0;
//计算矩形的面积
int rect(int a,int b){
static int count=0;
count++;
global++;
int s=a*b;
return s;
}
//计算正方形的面积
int quadrate(int a){
static int count=0;
count++;
global++;
int s=rect(a,a);
return s;
}
int main(){
int a=3;
int b=4;
int *pa;
pa = &a;
int *pb=&b;
int *pglobal=&global;
int (*pquadrate)(int a)=&quadrate;
int s=quadrate(a);
printf("%d\n",s);
}
wq-->gcc -g main.c -o main.out-->gdb main.out-->start-->n-->n-->n-->...
(gdb) p a $1 = 3 (gdb) p &a $2 = (int *) 0x7fffffffde9c (gdb) p pa $3 = (int *) 0x7fffffffde9c (gdb) p *pa $4 = 3
/*连续声明了a,b两个变量,为什么不连续?
a,b 都是整型数据,占32位,即4个字节,默认打印所占的第一个字节,a所占的(de9c,de9d,de9e,de9f),所以到b应该是(de9g),但十六机制满16进一,所以,变为(a1,dea1,dea2,dea3);*/
db) p &a
$4 = (int *) 0x7fffffffde9c
(gdb) p &b
$5 = (int *) 0x7fffffffdea0
/*pa的地址为s的地址增加4后的地址,但pa,pb,pglobal为内存地址,在64位地址总线下,一个内存地址,占8个字节*/
(gdb) p &pa
$6 = (int **) 0x7fffffffdea8
(gdb) p &pb
$7 = (int **) 0x7fffffffdeb0
(gdb) p &pglobalneicunneicun
$10 = (int **) 0x7fffffffdeb8
/*s的地址是b地址加+4后的地址;
gcc编译器进行优化,当我们在一个函数中,有若干个整型、浮点型等或其他类型变量时,gcc 会将相同类型的变量先声明,在声明其他类型的变量,所以先声明了所有的整形变量,再声明的指针变量!*/
(gdb) p &s
$9 = (int *) 0x7fffffffdea4
/*函数也是一个内存地址,所占也是8个字节*/
(gdb) p &pquadrateneicunneicun
$1neicun1 = (int (**)(int)) 0x7fffffffdec0
neicun
栈:保存的是函数运行时的状态;neicun
包括代码执行的行数,每个内存空间中的具体数值,在栈内一个函数可被多次调用,每次调用就是一个新的栈,
代码段和数据段都是从下向上分配,内存地址越来越大;
栈相反,最先分配的栈地址最大,栈的特点:先进后出
/*栈内是先进后出,最先分配的地址最大,以下可以看书,main的内存地址最大,向上依次减少*/
(gdb) bt
#0 rect (a=3, b=3) at main.c:7
#1 0x0000555555554719 in quadrate (a=3) at main.c:18
#2 0x0000555555554776 in main () at main.c:31
(gdb) p &rect
$5 = (int (*)(int, int)) 0x5555555546aa <rect>
(gdb) p &quadrate
$6 = (int (*)(int)) 0x5555555546e1 <quadrate>
(gdb) p &main
$7 = (int (*)()) 0x555555554721 <main>
/*以下结果可以看出,在两个函数中,count值不相同,global值相同
1. 因为count 是局部静态变量,global是全局变量;静态局部变量是某个函数总特有的,全局变量是所有函数共有的;
2. 静态变量,常量,全局变量都在数据段中;
3. 即使一个函数被调用多次,其中的静态变量还是指向数据段中的一个固定的地址,虽然不同函数的count值不同,但同一个函数中的count值不管被调用几次,指向地址不会改变;
*/
(gdb) f 0
#0 rect (a=3, b=3) at main.c:7
7 count++;
(gdb) p &count
$1 = (int *) 0x555555755018 <count>
(gdb) p &global
$2 = (int *) 0x555555755014 <global>
(gdb) f 1
#1 0x0000555555554719 in quadrate (a=3) at main.c:18
18 int s=rect(a,a);
(gdb) p &count
$3 = (int *) 0x55555575501c <count>
(gdb) p &global
$4 = (int *) 0x555555755014 <global>