【C基础】

1.内存图示:

在这里插入图片描述

1G内核(不动的,订书夹)栈空间(存放自动变量)自动变量:用的时候申请空间,用完的时候就自动释放,即:(outo)int a;(outo)int b(4);(静态) 存放全局变量 静态全局变量 静态局部变量 bss:未初始化的静态变量(数据) 代码中即static int a;static int b(4) RW data:可读可写的数据段 (区) 进程一出现就分配空间,直到整个进程结束才清空 RO data: 只读段:字符串常量(static) Text: 只读段(代码)

理解地址在内存空间如何存放的代码:
#include <stdio.h>

int g_init = 100;
int g_uninit;
static int sg_init = 200;
static int sg_uninit;

void fun()
{
int l_init = 100;
int l_uninit;
static int sl_init = 200;
static int sl_uninit;

printf("g_init:   %-8p\n", &g_init);
printf("g_uninit: %-8p\n", &g_uninit);
printf("sg_init:  %-8p\n", &sg_init);
printf("sg_uninit:%-8p\n", &sg_uninit);
printf("l_init:   %-8p\n", &l_init);
printf("l_uninit: %-8p\n", &l_uninit);
printf("sl_init:  %-8p\n", &sl_init);
printf("sl_uninit:%-8p\n", &sl_uninit);
printf("rodata    %-8p\n", "helloworld");
printf("text      %-8p\n", fun);

}

int main()
{
fun();
return 0;
}
运行结果:
lh@ubuntu:~$ ./a.out
g_init: 0x804a020
g_uninit: 0x804a038
sg_init: 0x804a024
sg_uninit:0x804a030
l_init: 0xbffd9998
l_uninit: 0xbffd999c
sl_init: 0x804a028
sl_uninit:0x804a034
rodata 0x8048620
text 0x804841d

区分static与 const:
static是静态变量,是可以改动的,但是const是只读变量,不能再给a赋值,(const 变量只能在定义时赋值,然后不能再改变其值)但可以通过改变a地址的内容改变a的值。数组越界的情况下也会改变。
#include<stdio.h>

int main()
{
const int a = 100;
int *p = &a;
*p = 250;

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

  return 0;

}

编译后:6 17 C:\Users\13712\Desktop\zz.c [Warning] initialization discards ‘const’ qualifier from pointer target type
运行结果:
250

                        --------------------------------
                       Process exited after 0.02551 seconds with return value 0
                       请按任意键继续. . .

#include<stdio.h>

int main()
{
const int a = 100;
int b[3];
printf(“%p\n”,&a);
printf(“%p\n”,b);

b[3] = 250;
printf("%d\n",a);


return 0;

}

在这里插入图片描述

b的首地址是…40,接着保存b1 b2 b3,然后从…4c开始放a,b(3)覆盖了常量a的值,(出现在windows系统中)。

理解static:

#include<stdio.h>

int add(int a,int b)

{
static int total = 0;

total += a + b;

return total;

}

int main()
{
printf(“%d\n”,add(3,5));
printf(“%d\n”,add(3,5));
printf(“%d\n”,add(3,5));
return 0;
}

结果:
在这里插入图片描述

97什么是g-a?
补码 原码 反码:

各种数据类型及其大小和表示范围:

类型存储大小值范围char1 字节-127——128 0——255unsigned char1 字节0——255signed char1 字节-128——127int2 字节或4字节-32768——32767或-2147483648——2147483647unsigned int2 字节或4字节0——65535或0——4294967295short2 字节-32768——32767unsigned short2 字节0——65535long4 字节-2147483648——2147483647unsigned long4 字节0——4294967295

判断数据类型长度符的关键字
用法
sizeof(类型说明符,数组名或表达式);
  或sizeof 变量名
定义
sizeof是C/C++中的一个操作符(operator),简单的说其作用就是返回一个对象或者类型所占的内存字节数。
MSDN上的解释为:
The sizeof keyword gives the amount of storage, in bytes, associated with a variable or a type(including aggregate types). This keyword returns a value of type size_t.
其返回值类型为size_t,在头文件stddef.h中定义。这是一个依赖于编译系统的值,一般定义为

1typedef unsigned int size_t;

世上编译器林林总总,但作为一个规范,它们都会保证char、signed char和unsigned
char的sizeof值为1,毕竟char是我们编程能用的最小数据类型。
语法
sizeof有三种语法形式,如下:

1 sizeof(object);//sizeof(对象);2 sizeof(type_name);//sizeof(类型);3 sizeofobject;//sizeof对象;

所以:

1 int i;2 sizeof(i);//ok3 sizeof i;//ok4 sizeof(int);//ok5 sizeofint;//error

既然写法3可以用写法1代替,为求形式统一以及减少我们大脑的负担,第3种写法,忘掉它吧!实际上,sizeof计算对象的大小也是转换成对对象类型的计算,也就是说,同种类型的不同对象其sizeof值都是一致的。
即sizeof可以对一个表达式求值,编译器根据表达式的最终结果类型来确定大小,一般不会对表达式进行计算。如:

sizeof(2);//2的类型为int,所以等价于sizeof(int);sizeof(2+3.14);//3.14的类型为double,2也会被提升成double类型,所以等价于sizeof(double);

浮点数的精度与范围&sizeof函数
#include <stdio.h>
#include <float.h>//提供了浮点型的范围和精度的宏;没有类型和函数的定义,一般用于数值分析

int main()
{
printf(“float 存储最大字节数 :%u\n”, sizeof(float));
printf(“float 最小值 :%E\n”, FLT_MIN );
printf(“float 最大值 :%E\n”, FLT_MAX );
printf(“精度值 : %d\n”, FLT_DIG );

printf("double 存储最大字节数 :%u\n", sizeof(double));
printf("double 最小值 :%E\n", DBL_MIN );
printf("double 最大值 :%E\n", DBL_MAX );
printf("精度值 : %d\n", DBL_DIG );



 printf("long double 存储最大字节数 :%u\n", sizeof(long double ));
printf("long double  最小值 :%LE\n", LDBL_MIN );
printf("long double  最大值 :%LE\n", LDBL_MAX );
printf("精度值 : %d\n", LDBL_DIG );

return 0;

}

结果:
float 存储最大字节数 :4
float 最小值 :1.175494E-038
float 最大值 :3.402823E+038
精度值 : 6
double 存储最大字节数 :8
double 最小值 :2.225074E-308
double 最大值 :1.797693E+308
精度值 : 15
long double 存储最大字节数 :16
long double 最小值 :3.205308E-317
long double 最大值 :3.205308E-317
精度值 : 18


Process exited after 0.02124 seconds with return value 0
请按任意键继续. . .

PC机上整型数类型大小及浮点数情况:

类型PC机上Linux系统char8int32short16long32long long64
float与double的精度理解:(整型数有范围,浮点数要精度)
类型PC机上Linux系统float6位-37到38double15位-307到308long double18位-4931到4932
上面的一行代表小数点后位数;下面的一行代表指数的范围(以10为基数)

通常:char(1)<short()<=int(4)<=long<longlong(8)
a=b=c(有可能a!=b!=c也有可能abc,abc的类型可能不同,尽量不要做类型转换,有风险,向上转换没风险,向下转换有溢出风险风险是什么?怎么转换?

浮点数的陷阱:
在这里插入图片描述

分析理解:当if语句在执行(f==123.456)的判断时,实际上是等号两边的数二进制形式的比较,f是float型,计算机先要把0.456转化成二进制表达,而等号右边的123.456默认是double型,计算机也要把他转化成二进制,然后去比较。但是float精度是六位,2^-1=0.5 2^-2=0.25 2^-3=0.125 2^-4=0.0625…float精确到小数点后6,不能把0.456用二进制精确表达,他的二进制表达转化成十进制后,是123.456001但是double却到了小数点后15位,他的二进制转换精度高,而且能把0.456精确表达,他的二进制表达转化成十进制后,是123.456。
解决办法:
在这里插入图片描述

浮点数的内存布局:

在这里插入图片描述

对typedef的理解:( 16系统intl两位 32位系统int四位)
工程包括两个文件夹,一个是与平台相关的,一个是与平台无关的,在与平台相关的文件中,普遍使用与平台无关文件中定义的数据类型,比如在与平台无关的文件中有typedef long double REAL,在与平台相关的文件里就用REAL代替long double来定义各种常量变量,如果换一个平台,那么就可以只通过改变与平台无关的文件夹里的REAL的数据类型,就可以实现平台的转换,增强了文件的可移植性。
用typedef来定义与平台无关的类型。
比如定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型为:
typedef long double REAL;
在不支持 long double 的平台二上,改为:
typedef double REAL;
在连 double 都不支持的平台三上,改为:
typedef float REAL;
也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。
标准库就广泛使用了这个技巧,比如size_t。
另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健(虽然用宏有时也可以完成以上的用途)

1.C.SWP:(待操作)
E325: ATTENTION
Found a swap file by the name “.1.c.swp”
owned by: root dated: Wed Dec 6 12:42:36 2017
file name: /tjy/11.18/1.c
modified: YES
user name: root host name: localhost.localdomain
process ID: 7751
While opening file “1.c”
dated: Tue Dec 5 18:54:50 2017
(1) Another program may be editing the same file.
If this is the case, be careful not to end up with two
different instances of the same file when making changes.
Quit, or continue with caution.
(2) An edit session for this file crashed.
If this is the case, use “:recover” or “vim -r 1.c”
to recover the changes (see “:help recovery”).
If you did this already, delete the swap file “.1.c.swp”
to avoid this message.
“1.c” 14L, 184C
Press ENTER or type command to continue
如果你是学C语言的新手,相信你一定遇到过这个问题,就是在你每次想要在终端打开源文件的时候会跳出来这些你看不懂的英文,下面我就来解释下它的意思以及产生的原因,最后怎么改正。
翻译:
E325: 注意
发现交换文件 “.1.c.swp”
所有者: root 日期: Wed Dec 6 12:42:36 2017
文件名: /tjy/11.18/1.c
修改过: 是
用户名: root 主机名: localhost.localdomain
进程 ID: 7751
正在打开文件 “1.c”
日期: Tue Dec 5 18:54:50 2017
(1) 另一个程序可能也在编辑同一个文件。
如果是这样,修改时请注意避免同一个文件产生两个不同的版本。
退出,或小心地继续。

(2) 上次编辑此文件时崩溃。
如果是这样,请用 “:recover” 或 “vim -r 1.c”
恢复修改的内容 (请见 “:help recovery”)。
如果你已经进行了恢复,请删除交换文件 “.1.c.swp”
以避免再看到此消息。
交换文件 “.1.c.swp” 已存在!
以只读方式打开([O]), 直接编辑((E)), 恢复(®), 删除交换文件((D)), 退出((Q)), 中>
– 更多 –
我这里建立的一个1.c的文件,它的意思就是就是产生了一个 .1.c.swp的文件,与另一个程序编辑的文件同名,导致出错。
为什么会这样呢?
因为你上次在终端修改了它的内容未做保存就退出了,所以导致在下次运行时会出错。
怎么修改呢?
翻译已经告诉你该怎么做了,就是要删除 .1.c.swp这个文件,我们可以先在终端输入 ls -a来查看到这个文件,然后输入 rm -f .1.c.swp 这个命令来实现对 .1.c.swp这个文件的删除。

段错误

情况1.代码示意:
#include<stdio.h>
int main()
{
char *p =“hello world”;

*p = ‘H’;

return 0;

}
运行结果
在这里插入图片描述

字符串:hello world分配在只可读的rodata区域,p是hello wrold的首地址, *p表示首字母h,*p=H是要把h变成H,不可以这样

大小写转换:
A------65 a------- 97
B------66 b-------98
C------67 c-------99
得出:
c+(’ A’-‘a’)=C
a+(’ A’-‘a’)=A
b+(’ A’-‘a’)=B(不要用A-a=32这个魔鬼数字)
进一步得到:
C-(’ A’-‘a’)=c
B-(’ A’-‘a’)=b
字符数字与整形数字的转换:
‘0’----------48
‘1’----------49
‘2’----------50
*******
‘9’----------57
得出:
‘9’-‘0’=9
‘0’-‘0’=0
‘1’-‘0’=1
进一步得到:
‘9’= 9+‘0’
‘0’=0+‘0’
‘1’=1+‘0’

将一个数用二进制表达出来的代码:
#include<stdio.h>
int main ()
{
int a[32];
int n = 128;
int i = 0;
int j = 0;
for(i = 0;i <= 31;i++)
{
a[i] = n % 2;
n=n/2;
}
for(i = 31;i >= 0;i–)
{
printf(“%d”,a[i]);
if((++j)%4 == 0)
{
printf(" ");

    }
}

}

十进制转到二进制:
#include <stdio.h>
int main()
{

int a = 0b10;
printf("%d\n", a);
return 0;

}
二进制到十进制:

数组越界:你犯了一个系统不提示的错误
#include<stdio.h>

int main()
{
const int a = 100;
int b[3];
printf(“%p\n”,&a);
printf(“%p\n”,b);

b[3] = 250;
printf("%d\n",a);


return 0;

}

b的首地址是…40,接着保存b1 b2 b3,然后从…4c开始放a,b(3)覆盖了常量a的值,(出现在windows系统中)。

通过指针把字符串传入空数组中:
#include <stdio.h>

char *my_strcpy(char dest[],char *src)
{
char *dest_0 = dest;
while (*src != ‘\0’)
{

     *dest = *src;
     dest++;
     src++;

}
*dest = '\0';

return dest_0;

}

int main()
{
char a[20];
char *p = “hello”;

 printf("%s\n",my_strcpy(a,p));
 
 return 0;

}
对比

#include <stdio.h>

char *my_strcpy(char dest[],char *src)
{

while (*src != '\0')
{

     *dest = *src;
     dest++;
     src++;

}
*dest = '\0';

return dest;

}

int main()
{
char a[20];
char *p = “hello”;
my_strcpy(a,p);
printf(“%s\n”,a);

 return 0;

}

判断两个字符串是否相等:
#include <stdio.h>
int main()
{
char *p = “asafg”;
char *q = “asdfgcv”;
int i = 0;
int j = 0;
while(p[j] != ‘\0’ && p[i] !=‘\0’)
{
if(p[i] == q[j])
{
i++;
j++;
}
else
{
break;
}

        }  
         if(p[i] == q[j])
         {
             printf("%d\n",1);
         }
         else
         {
              printf("%d\n",0);
         }
         
         
         
}

单元测试用例:
#include <stdio.h>
#include <stdlib.h>//此函数库中包含atoi()函数,比较atoi函数功能与自己定义的my_atoi函数功能
int my_atoi(char *str)
{
int total = 0;
int flag = 1;
while(*str == ’ ')
{
str++;
}
if(*str == ‘-’)
{
flag = -1;
str++;
}
else if(*str == ‘+’)
{
str++;
}
while((*str >= ‘0’) && (*str <= ‘9’))
{
total = total * 10 +(*str-‘0’);
str++;
}
return total * flag;
}
int main()
{
char *p = " -123asd123";
printf(“%d\n”,my_atoi§);
printf(“%d\n”,atoi§);
return 0;

}

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>/此函数库中包含atoi()函数,比较atoi函数功能与自己定义的my_atoi函数功能
double my_atoi(char str)/
{
double total = 0;/
(这里的double表示返回值的类型*/
int flag = 1;
while(*str == ’ ')
{
str++;
}
if(*str == ‘-’)
{
flag = -1;
str++;
}
else if(*str == ‘+’)
{
str++;
}
while((*str >= ‘0’) && (*str <= ‘9’))
{
total = total * 10 +(*str-‘0’);
str++;
}
while(*str == ‘.’)
{
str++;
}
double build_decimal = 1 ;
double decimal = 0;
while((*str >= ‘0’) && (*str <= ‘9’))
{
build_decimal = build_decimal * 10;
decimal = decimal +(*str-‘0’) / build_decimal;
str++;
}
return (total+decimal) * flag;
}
int main()
{
char *p = " -123.375abc";
printf(“%lf\n”,my_atoi§);
printf(“%d\n”,atoi§);
return 0;

}

在这里插入图片描述

指针的三种表达方式:

a[0] a[1] a[2]*(p + 0) (p + 1) (p + 2)(a +0)(a + 1) *(a + 2)
千万不可以 *a[0]
定义指针类型的理解:
int *p = &a == int *p ; p = &a;

例子:

在这里插入图片描述

从字符串123
到数字123
在这里插入图片描述

在这里插入图片描述

错误:数组名不能做左值但是*p 和 *a ,a[0] 因为p是变量所以也可以
在这里插入图片描述

对比
在这里插入图片描述

再对比:
在这里插入图片描述
在这里插入图片描述

字符串是定义在rodata段的,通过数组定义的字符串会在栈空间形成备份,通过定义字符数组达到修改字符串内容
指针保存的是字符串的地址字符串的内容是在rodate段

在这里插入图片描述

40!(迭代法)
#include<stdio.h>
#define FACT_LEN 60
int *get_one(int *total,int len,int n)
{

int carry = 0;
int i;
for(i = len-1;i >= 0;i--)
{
    total[i] = total[i]*n + carry;
    carry = total[i]/10;
    total[i] = total[i] % 10;
}
return total;

}
void get_fact(int *total,int len)
{
int n;
for(n = 1;n <= 40;n++)
{
get_one(total,len,n);
}

}
void printf_array(int*total,int len)
{
int i;
for(i=0;i < len - 1;i++)
{

 printf("%d",total[i]);
}

}
int main()

{
int total[FACT_LEN];
int len = FACT_LEN;
int i ;
for(i = 0;i < FACT_LEN;i++)
{
total[i] = 0;
}
total[59] = 1;
get_fact(total,len);
printf_array(total,len);
}
对比输出位置:
#include<stdio.h>
#define FACT_LEN 60
int *get_one(int *total,int len,int n)
{

int carry = 0;
int i;
for(i = len-1;i >= 0;i--)
{
    total[i] = total[i]*n + carry;
    carry = total[i]/10;
    total[i] = total[i] % 10;
}
return total;

}
void get_fact(int *total,int len)
{
int n;
for(n = 1;n <= 40;n++)
{
get_one(total,len,n);
printf_array(total,len);
printf(“\n”) ;
}

}
void printf_array(int*total,int len)
{
int i;
for(i=0;i < len - 1;i++)
{

 printf("%d",total[i]);
}

}
int main()

{
int total[FACT_LEN];
int len = FACT_LEN;
int i ;
for(i = 0;i < FACT_LEN;i++)
{
total[i] = 0;
}
total[59] = 1;
get_fact(total,len);

}
询问返回地址:
#include <stdio.h>

#define FACT_LEN 80

int * get_one(int *total, int len, int n)
{
int i;
int carry = 0;

for (i = 0; i < len; i++)
{
    total[i] = total[i] * n + carry;
    carry = total[i] / 10;
    total[i] = total[i] % 10;
}

return total;

}

int *get_fact(int *total, int len, int n)
{
int i;

for (i = 0; i < len; i++)
{
    total[i] = 0;
}
total[0] = 1;

for (i = 1; i <= n; i++)
{
    get_one(total, len, i);
}
  • return total;
    }

void print_array(int *array, int len)
{
int i;

for (i = len - 1; i >= 0; i--)
{
    printf("%d", array[i]);
}
printf("\n");

}#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int d = 0;
void fun_my(int a, int b)
{

    if(a ==b)
{
  printf("你是啥呀?");
  printf("用了%d\n次",d+1);    
}
else if(a > b)
{

    printf("Are you swine brain? big pig\n");
    printf("Please tell me your number again :");
    scanf("%d",&a);
    d++;
    fun_my(a,b);
}
else
{
    printf("Are you swine brain? small+ pig\n");
    printf("Please tell me your number again :");
    scanf("%d",&a);
    d++;
    fun_my(a,b);
    
}

}
int main()
{
int a;
int b;

srand(time(NULL));
b = rand()%100+1;
printf("%d\n",b);
printf("Please tell me your number :");
scanf("%d",&a);
{
    fun_my(a ,b);
}

}

倒序输出字符串:
方法一
#include<stdio.h>
#include<string.h>
void dao_xu(char *a,int begain,int end)
{
char tem;
if(begain > end)
{
printf(“%s\n”,a);
return;//return与exit的区别是啥?return是返回到上次调用的地方,但是exit是结束整个程序。
}
tem = a[begain];
a[begain] = a[end];
a[end] = tem;
dao_xu(a,begain+1,end-1) ;
return;
}
int main()
{
char a[] = “12345678”;
int len =strlen(a);
int end = len - 1;//字符串长度测出来不带/0的长度
int begain = 0;
dao_xu(a,begain,end);

}
方法二:
#include<stdio.h>
#include <string.h>
void reverse_my(char a,int len)
{
int tmp;
if(len <=1)
{
//printf(“%s\n”,a);(a代表首地址,在子函数中不断变化,在这里输出时,a=4)
return;
}
tmp = a[0];
a[0] = a[len - 1];
a[len - 1] = tmp;
reverse_my (a+1,len-2);
return;
}
int main()
{
char a[] = “12345678”;
int len = strlen(a);
reverse_my(a,len);
printf(“%s\n”,a);/
(a代表首地址,在子函数中不断变化,但a在主函数中不变化,在这里输出时,a仍然是整个字符串的首地址,传上去的首地址,是为了子函数通过地址间接对主函数的字符串操作,主函数的a与子函数
的a的值不一样的 */
}

自己辨认一下else是和哪一个if搭配的;会发现上面的比较好;else和第二个(离的近的)if搭配;所以上面的函数书写可以看的清楚一点;最好是加上大括号,这俩都没有加。

位操作练习:

  1. 题目:请编写一个 c 函数,该函数给出一个字节中被置为 1 的位的个数.
    自己的思路:
    #include<stdio.h>
    int main()
    {
    unsigned int a , total , b, mask;
    a = 0x00000121;
    mask = 0x00000001;
    total = 0;
    int i;

    for(i = 0 ;i <= 31;i++)
    {
    b = ( mask << i);
    if (b & a )
    {
    total++;
    }
    }

    printf(“%d\n”,total);

}
老师的讲课:
方法一:
#include<stdio.h>
int count_bit(unsigned int a)
{
int total = 0;
while(a > 0)
{
if(a % 2 == 1)
{
total++;
}
a = a / 2;
}
return total;

}
int main()
{
printf(“%d\n”,count_bit(0x13));
}
方法二:
#include<stdio.h>
int count_bit(unsigned int a)
{
unsigned int mask = 1u;
int total = 0;
while(a > 0)
{
if(a & mask == 1)
{
total++;
}
a = a >> 1;
}
return total;

}
int main()
{
printf(“%d\n”,count_bit(0x13));
}
方法三:
#include<stdio.h>
int count_bit(unsigned int a)
{
unsigned int mask = 1u;
int total = 0;
while(a > 0)
{
total += a & mask;
a = a >> 1;
}
return total;

}
int main()
{
printf(“%d\n”,count_bit(0x13));
}
循环移位:
自己的:
#include<stdio.h>
int main()
{
unsigned int a, b, mask ;
a = 0x11;
int i;
for (i=7;i>0;i–)
{
b = a % 2;
if(b == 1)
{
a = a / 2 + pow(2,7);

   }
   else
   {
       a = a / 2;
   }
}

printf("%x\n",a);

}
老师的:
#include<stdio.h>
int ror(unsigned int x,int n)
{
int i;
for(i=1;i<=n;i++)
{
x = (x<<31) | (x>>1);//循环移位的金典步骤
}
return x;
}
int main ()
{
unsigned int x = 0xdeadbeef;//不要把16进制的数 当作了字符串
printf(“%x\n”,x);
printf(“%x\n”,ror(x,8));
}
补充:通过位操作输出一个数的二进制形式
#include<stdio.h>
int printf_bin(unsigned int x)
{
unsigned int mask = 1u;
int i;
int xunhuanshu = 0;
for( i = 31; i >= 0 ; i–)//最低为是不是1也要判断,不要漏了
{
xunhuanshu++;
if ((mask << i) & x)
{
printf(“%c”,‘1’);
}
else
{
printf(“%c”,‘0’);
}
if(xunhuanshu % 4 == 0)
{
printf(" ");//i从31到0总共进行了32次循环,可以输出32个数字(0-1),可以每输出4个,出一空格
}
}
}

int main()
{
unsigned int x = 0x00000011;
printf (“%x”,x);
printf(“\n”);
printf_bin(x);
}
循环移位升级:
#include<stdio.h>
int ror(unsigned int x,int n)
{
int i;
while(n < 0)
{
n = n+32;
}
n = n % 32;
for(i=1;i<=n;i++)
{
x = (x<<31) | (x>>1);//循环移位的金典步骤
}
return x;
}
int main ()
{
int n;
unsigned int x = 0xdeeddaed;//不要把16进制的数 当作了字符串
printf(“%x\n”,x);
scanf(“%d”,&n);
printf(“%x\n”,ror(x,n));
}
循环移位再升级:
#include<stdio.h>
int ror(unsigned int x,int n)
{
int i;
while(n < 0)
{
n = n+32;
}
n = n % 32;
return x << (32 - n) | (x >> n);
}
int main ()
{
int n;
unsigned int x = 0xdeeddaed;//不要把16进制的数 当作了字符串
printf(“%x\n”,x);
scanf(“%d”,&n);
printf(“%x\n”,ror(x,n));
}
掩码例子(取出固定的几位显示出来)
#include<stdio.h>
int get_bits(unsigned int x,int p1,int p2)
{
return (x >> p1) & (0u <<(p2 - p1 +1));/*在这里你 x 向右移动4(p1)位,你是要移动后的末尾开始提取数字,相当于你把0111中的最后一个1当成了 0001
0010 0011 0100 0101 0110 01110 1000的第四位,所以4-12是101100111 */
}
int main ()
{
unsigned int a = 0x12345678;//0001 0010 0011 0100 0101 0110 0111 1000 — 167 // 0001 0110 0111
printf(“%x\n”,get_bits(a, 4, 12));
}

运算符优先级:
在这里插入图片描述

指针理解
#include <stdio.h>

char *my_strcpy(char dest[],char *src)
{
dest[0] = ‘H’;
printf(“%c\n”,dest[0]);
// *dest = “SHE”;//右边表示的是一个地址;左边是指针变量 ,*dest类似于int a = 45中的 a;它不是描述地址的变量,*dest中的dest表示的是地址;
//printf(“%s\n”,dest);
*dest = ‘S’;
printf(“%c\n”,*dest);/*可以输出S的,‘S’是一个变量,dest是一个地址,*dest等价于在dest那个地址定义了一个指针变量,
可以通过给’*dest’赋值改变这个地址空间的内容。但是不能用字符串赋值,字符串表示的是一个地址,即字符串的首地址 */
dest = “Hell”;//字符串在这里表示的是地址,dest也是地址,两边的变量类型是一样的
printf(“%s\n”,dest);

}

a++与++a:
a++是先执行表达式再自增,执行表达式时使用的是a的原值,++a是先自增再执行表达式,执行表达式时使用的是·自增后的a.
#include<stdio.h>
int main()
{
int a = 0;
printf(“%d\n”,a++);//输出0,执行完后a=1;
printf(“%d\n”,a);
}

#include<stdio.h>
int main()
{
int a = 0;
printf(“%d\n”,++a);//输出1,执行完后a=1;
printf(“%d\n”,a);
}

总结:
a++先使用a的值再自增,产生临时变量,++a先自增,再使用新的a的值,不产生临时变量。程序是按照步骤来的,如果是a++的话,在那一行代码中,a的值是不变的,下一行才发生变化,++a则是在拿一行已经发生了变化。

各进制的定义与输出:
#include<stdio.h>
int main()
{
int a = 0b10;
printf(“a= %d\n”,a);

int b = 010;
printf("b= %d\n",b);

int c = 10;
printf("c= %d\n",c);

int d = 0x10;
printf("d= %d\n",d);

return 0;

}

格式说明由“%”和格式字符组成,如%d%f等。它的作用是将输出的数据转换为指定的格式输出。格式说明总是由“%”字符开始的。不同类型的数据用不同的格式字符。
格式字符有d,o,x,u,c,s,f,e,g等。

%d整型输出,%ld长整型输出,
%o以八进制数形式输出整数,
%x以十六进制数形式输出整数,
%u以十进制数输出unsigned型数据(无符号数)。
%c用来输出一个字符,
%s用来输出一个字符串,
%f用来输出实数,以小数形式输出,(备注:浮点数是不能定义如的精度的,所以“%6.2f”这种写法是“错误的”!!!)
%e以指数形式输出实数,
%g根据大小自动选f格式或e格式,且不输出无意义的零。
scanf(控制字符,地址列表)
格式字符的含义同printf函数,地址列表是由若干个地址组成的表列,可以是变量的地址,或字符串的首地址。如scanf(“%d%c%s”,&a,&b,str);
指针
指针基础知识详解:
1.01 地址和指针的概念。
要想明白指针,就要弄清楚数据时怎样在内存中储存和读取的。在程序中一般是通过变量名来对内存单元来进行存取的。其实,程序经过编译过已经将变量名转化为变量的地址,对变量值的存取就是通过地址进行的。由于通过地址能找到所需的变量单元,我们可以说地址指向该变量单元。因此在C语言中将地址形象化成指针。一个变量的地址称为该变量的指针。
如果有一个变量专门用来存放另一变量的地址(即指针),则称它为指针变量。指针是一个地址,而指针变量时存放地址的变量。
1.02 定义指针变量
基类型 指针变量名;
下面的定义都是合法的:
Float a;
Char b;
可以用赋值语句使一个指针变量得到另一个变量的地址,从而使它指向该变量。例如:
P=&i;将变量i的地址放到指针变量p中,因此p就指向了变量i;
P2=&j;
定义指针变量时要注意:
1)指针变量前面的“
”表示该变量的类型为指针型变量。指针变量名为p和p2而不是
p和
p2;
2)在定义指针变量时必须指定基类型。
3)一个指针变量只能指向同一个类型的变量,
1.03 指针变量的引用
特别要记住,指针变量只能存放地址,而不能将任何非地址类型的数据赋给一个指针变量。
关于和&的关系:
1)&p的含义。&和是同级运算符,但是按照自右向左的运算方向结合;
如果有p_2=&p_1.它的作用是将&a(a的地址)赋给p_2.(p_1是a的地址;p_1就是a)
2)
&a的含义是先进行&a运算,得到a的地址,在进行
运算。即&a指向的变量,也就是变量a。
3)若
P=a,(*p)++相当于a++,括号是必要的。
1.04 数组与指针
一个变量有地址,一个数组包含若干元素,它们都有相应的地址。
所谓的数组元素的指针就是数组元素的地址。
对指针变量赋值:
Int a[10]
Int *p
p=&a[0];
把a[0]元素的地址赋给指针变量p;
C语言规定数组名(不包括形参数组名,形参数组名并不占据实际的内存空间)代表数组中首元素的地址:下面两个是等价的:
P=&a[0];
P=a; 此时的数组名不代表整个数组;
在定义指针变量时了以对其赋值:
Int *p=&a[0];
等价于:
Int *p;
P=&a[0];
当然定义时也可以写成:
Int *p=a;
1.05 通过指针引用数组元素
这个地方就说重点的吧。
如果p的初值为&a[0],,则:
1)p+i和a+i就是a[i]的地址,它们指向a数组的第i个元素。这里注意:a代表首元素的地址,a+i也是地址。
2) *(p+i)或 (a+i)是p+i或a+i所指向的数组元素,即a[i]。 可以看出,[ ]实际上是变址运算符,即a[i]按a+i 计算地址,然后再找出地址单元中的值。
3)指向数组的指针也可以带下标,如p[i]与
(p+i)等价。
在形参中;数组名退化成指针:
#include<stdio.h>

char *my_strcpy(char dest[20],char *src)//这里的dest[20]并不是数组,而是指针
{
printf(“%d\n”,sizeof(dest));//指针的字节数
}
int main()
{
char a[20] = “helloworld”;
printf(“%d\n”,sizeof(a));//此时a代表的是数组本身,数组的字节数
my_strcpy(a,“world”);//此时a代表的是数组的首地址

return 0;

}

#include<stdio.h>

char *my_strcpy(char dest[20],char *src)//这里的dest[20]并不是数组,而是指针
{
printf(“%d\n”,sizeof(dest));//指针的字节数
}
int main()
{
char a[20] = “helloworld”;
printf(“%s\n”,a);//a在这里表示数组的首地址
printf(“%d\n”,sizeof(a));//此时a代表的是数组本身,数组的字节数
my_strcpy(a,“world”);//此时a代表的是数组的首地址,传给形参
printf(“%d\n”,sizeof(a));//此时a代表的是数组本身,数组的字节数
return 0;
}

结果:
helloworld
20
8
20


Process exited after 0.03365 seconds with return value 0
请按任意键继续. . .

1.06 指针变量的运算。
如果先使p指向数组a的首元素(即p=a)则:
1)p++。使p指向下一个元素,即a[1]。
2)p++。由于++和同优先级,结合方向为自右向左,因此等价于*(p++).作用是先得到p指向的变量的值(即p),然后再使p+1=>p。
3)
(P++)与*(++p)的作用不同。
4)++(*p)表示p所指向的元素加1,如果p=a。则++(p)相当于++a[0]。注意:是元素值a[0]加1. 而不是指针p的值加1。
5)如果p当前指向a数组中的第i个元素。则
(p–)相当于a[i–];同样的有 *(++p)相当于a[++i];

认识指针类型:

int p; //这是一个普通的整型变量

int p; //首先从P 处开始,先与结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以P是一个返回整型数据的指针

int p[3]; //首先从P 处开始,先与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的,所以P 是一个由整型数据组成的数组

int p[3]; //首先从P 处开始,先与[]结合,因为其优先级比高,所以P 是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组 3个元素的数组,每个数组是指针

int (p)[3]; //首先从P 处开始,先与结合,说明P 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以P 是一个指向由整型数据组成的数组的指针,数组有三个元素

int **p; //首先从P 开始,先与结合,说是P 是一个指针,然后再与结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针.

int p(int); //从P 处起,先与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据

Int (*p)(int); //从P 处开始,先与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针

int (p(int))[3]; //可以先跳过,不看这个类型,过于复杂从P 开始,先与()结合,说明P 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的结合,说明函数返回的是一个指针,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据.所以P 是一个参数为一个整数据且返回一个指向由整型指针变

指针的三种表达方式:
a[0] a[1] a[2]*(p + 0) (p + 1) (p + 2)(a +0)(a + 1) *(a + 2)
千万不可以 *a[0]
定义指针类型的理解:
int *p = &a == int *p ; p = &a;

例子:
#include<stdio.h>
int main()
{

int a = 100;
int *p = &a;//int *p     p = &a ;而且此处的*意思是说明p是个指针类型的变量,p地址内存空间处存放a的地址
printf("输出a的值%d\n",p);
printf("输出a的值%d\n",a);
printf("输出a的值%d\n",*p);//这里的*可以理解成指向的意思,指向了p所代表的地址的内存空间;此时*p任然是一个变量,可以赋值,它就是p所指向的地址空间的变量;*p等价于a,1. p = &a 2.*p = *&a ==100


*p = 200;
printf("输出a的值%d\n",a);
printf("输出a的值%d\n",*p);


p[0] = 300;
printf("输出a的值%d\n",a);
printf("输出a的值%d\n",*p);
printf("输出a的值%d\n",p[0]);//p[0]等价于*p,等价于a;它们指向同一地址空间



return 0;

}
结果:
输出a的值6487620
输出a的值100
输出a的值100
输出a的值200
输出a的值200
输出a的值300
输出a的值300
输出a的值300


Process exited after 0.02897 seconds with return value 0
请按任意键继续. . .

#include<stdio.h>
int main()
{
char *p = “helloworld”;//字符串helloworld在这里其实是把helloworld的首地址给了指针变量P;但是字符串存放在rodata段,不可写,所以会出现段错误
*p = ‘H’;
printf(“%c\n”,*p);
return 0;
}

在这里插入图片描述

注意:编译时没有错误和警告
#include<stdio.h>

int main()
{
char a[] = “helloworld”;
printf(“%c\n”,a[0]);
a[0] = ‘H’;
printf(“%c\n”,a[0]);
return 0;
}

在这里插入图片描述

类比于:
#include<stdio.h>

int main()
{
char a[] = “helloworld”;//在栈空间对rodata段的字符串helloworld做了备份,rodata仅有一个helloworld
printf(“%c\n”,a[0]);
*a = ‘H’;
printf(“%s\n”,a);
return 0;
}
在这里插入图片描述

指针的好处:(交换函数swap)
不用指针:(想要在主函数的a b的内存空间内交换她俩的值,没有成功,而是在新空间内定义了另外的a和b,在子函数完成了他俩的交换)
void swap(int a,int b)//调用子函数,此时的a和b是新空间内的变量,对他进行的操作不影响主函数的a和b;
{
int tem;

printf("swap: %d %d\n",a,b);//输出3 5

tem = a;

a = b;

b = tem;

printf("swap : %d %d\n",a,b);//子函数完成了他俩的交换5 3,但是这里的a和b和主函数的a和b不一样。形参和实参内存空间不同,

}
int main()

{
int a = 3;

int b = 5;

printf("%d %d\n",a,b);//此时输出3 5 

swap(a,b);//调用swap函数

printf("%d %d",a,b);//经过swap函数后仍然是3 5 ,他输出的值是主函数定义a和b时候的地址空间的内容,此空间的内容没有发生交换。


return 0;

}
结果:
3 5
swap: 3 5
swap : 5 3
3 5

Process exited after 0.05202 seconds with return value 0
请按任意键继续. . .

通过指针:
#include<stdio.h>

void swap(int *a,int *b)//形参是指针,形参的操作是对主函数定义的a b的内存空间的内容的操作,可以完成主函数的a b 的内容的交换。
{
int tem;

printf("swap: %d %d\n",*a,*b);

tem = *a;

*a = *b;

*b = tem;

return a , b ;  

}
int main()

{
int a = 3;

int b = 5;

printf("%d %d\n",a,b);

swap(&a,&b);

printf("%d %d",a,b);


return 0;

}

结果:
3 5
swap: 3 5
5 3

Process exited after 0.0206 seconds with return value 0
请按任意键继续. . .
如果想把上面程序的tem换成*tem,则:
#include<stdio.h>

void swap(int *a,int *b)
{
int t;
int *tem = &t;
printf(“swap: %d %d\n”,*a,*b);

*tem = *a;

*a = *b;

*b = *tem;

return a , b ;  

}
int main()

{
int a = 3;

int b = 5;

printf("%d %d\n",a,b);

swap(&a,&b);

printf("%d %d",a,b);


return 0;

}

交换:
3 5
swap: 3 5
5 3

Process exited after 0.07138 seconds with return value 0
请按任意键继续. . .

详解C语言的类型转换
1.自动类型转换
字符型变量的值实质上是一个8位的整数值,因此取值范围一般是-128~127,char型变量也可以加修饰符unsigned,则unsigned char 型变量的取值范围是0~255(有些机器把char型当做unsighed char型对待, 取值范围总是0~255)。
如果一个运算符两边的运算数类型不同,先要将其转换为相同的类型,即较低类型转换为较高类型,然后再参加运算,转换规则如下图所示。
在这里插入图片描述

图中横向箭头表示必须的转换,如两个float型数参加运算,虽然它们类型相同,但仍要先转成double型再进行运算,结果亦为double型。 纵向箭头表示当运算符两边的运算数为不同类型时的转换,如一个long 型数据与一个int型数据一起运算,需要先将int型数据转换为long型, 然后两者再进行运算,结果为long型。所有这些转换都是由系统自动进行的, 使用时你只需从中了解结果的类型即可。这些转换可以说是自动的,当然,C语言也提供了以显式的形式强制转换类型的机制。
当较低类型的数据转换为较高类型时,一般只是形式上有所改变, 而不影响数据的实质内容, 而较高类型的数据转换为较低类型时则可能有些数据丢失。
在进行自动类型转换的时候,如果原来的数是无符号数,那么在扩展的时候,高位填充的是0;如果是有符号数,那么高位填充的时符号位!
2.赋值中的类型转换
当赋值运算符两边的运算对象类型不同时,将要发生类型转换, 转换的规则是:把赋值运算符右侧表达式的类型转换为左侧变量的类型。具体的转换如下:
(1) 浮点型与整型
将浮点数(单双精度)转换为整数时,将舍弃浮点数的小数部分, 只保留整数部分。将整型值赋给浮点型变量,数值不变,只将形式改为浮点形式, 即小数点后带若干个0。注意:赋值时的类型转换实际上是强制的。
(2) 单、双精度浮点型
由于C语言中的浮点值总是用双精度表示的,所以float 型数据只是在尾部加0延长为doub1e型数据参加运算,然后直接赋值。doub1e型数据转换为float型时,通过截尾数来实现,截断前要进行四舍五入操作。
(3) char型与int 型
int型数值赋给char型变量时,只保留其最低8位,高位部分舍弃。
chr型数值赋给int型变量时, 一些编译程序不管其值大小都作正数处理,而另一些编译程序在转换时,若char型数据值大于127,就作为负数处理。对于使用者来讲,如果原来char型数据取正值,转换后仍为正值;如果原来char型值可正可负,则转换后也仍然保持原值, 只是数据的内部表示形式有所不同。
(4) int型与1ong 型
long型数据赋给int型变量时,将低16位值送给int型变量,而将高16 位截断舍弃。(这里假定int型占两个字节)。将int型数据送给long型变量时,其外部值保持不变,而内部形式有所改变。
(5) 无符号整数
将一个unsigned 型数据赋给一个占据同样长度存储单元的整型变量时(如:unsigned→int、unsigned long→long,unsigned short→short) ,原值照赋,内部的存储方式不变,但外部值却可能改变。
将一个非unsigned整型数据赋给长度相同的unsigned型变量时, 内部存储形式不变,但外部表示时总是无符号
3.强制类型转换
可以利用强制类型转换运算符将一个表达式转换成所需类型:
例如:
(double)a (将a转换成double类型)
(int)(x+y) (将x+y的值转换成整型)
(float)(5%3) (将5%3的值转换成float型)
(int)(1.5+2.3) = 3
(int)1.5+2.3=?
C语言函数声明:
include<stdio.h>

int add(int a,int b)//正规的写法,主函数调用子函数add,被调用函数在调用函数的前面,这样做起到了声明和定义的作用,不需要再声明和定义

{
static int total = 0;

total += a + b;

return total;

}
int main()
{
printf(“%d\n”,add(3,5));
printf(“%d\n”,add(3,5));
printf(“%d\n”,add(3,5));
return 0;
}

结果:
在这里插入图片描述

#include<stdio.h>

int main()
{
printf(“%d\n”,add(3,5));
printf(“%d\n”,add(3,5));
printf(“%d\n”,add(3,5));
return 0;
}

int add(int a,int b)/不规范的写法却是正确的值,被调用函数(此处是子函数)在调用函数(主函数)的后面,因为编译器的隐式声明,在上面的 printf(“%d\n”,add(3,5));中,隐式声明默认add返回int型,恰好此处子函数返回int型/

{
static int total = 0;//所以得到了正确的值

total += a + b;

return total;

}

结果:

在这里插入图片描述

隐式声明出错:
#include<stdio.h>

int main()
{
printf(“%lf\n”,add(3.2,5.2));
printf(“%lf\n”,add(3.2,5.2));
printf(“%lf\n”,add(3.2,5.2));
return 0;
}

double add (double a,double b)//被调用函数(子函数)返回double型的数据,但是上面printf(“%lf\n”,add(3.2,5.2));经过隐式声明默认add返回的是整型;造成了冲突;于是 [Error] conflicting types for ‘add’;

{
static double total = 0;

total += a + b;

return total;

}

结果:
在这里插入图片描述

改正方法一:对被调用的函数做声明
#include<stdio.h>
extern double add() ;
int main()
{
printf(“%lf\n”,add(3.2,5.2));
printf(“%lf\n”,add(3.2,5.2));
printf(“%lf\n”,add(3.2,5.2));
return 0;
}

double add (double a,double b)//被调用函数(子函数)返回double型的数据,但是上面printf(“%lf\n”,add(3.2,5.2));经过隐式声明默认add返回的是整型;造成了冲突;于是 [Error] conflicting types for ‘add’;

{
static double total = 0;

total += a + b;

return total;

}
结果:
8.400000
16.800000
25.200000


Process exited after 0.05499 seconds with return value 0
请按任意键继续. . .

改正方法二:规范书写顺序,把被调用函数定义在调用函数前面,在定义的同时起到声明的作用
#include<stdio.h>
double add (double a,double b)//规范写法,函数声明有分号,这里没有分号
{
static double total = 0;

total += a + b;

return total;

}

int main()
{
printf(“%lf\n”,add(3.2,5.2));
printf(“%lf\n”,add(3.2,5.2));
printf(“%lf\n”,add(3.2,5.2));
return 0;
}
结果:
8.400000
16.800000
25.200000


Process exited after 0.02612 seconds with return value 0
请按任意键继续. . .
全局变量(Global Variable)
• 全局变量定义在所有的函数体之外,它们在程序开始运行 时分配存储空间,在程序结束时释放存储空间,在任何函 数中都可以访问全局变量
• 注意: 虽然全局变量用起来很方便,但一定要慎用,能用 函数传参代替的就不要用全局变量。 • 局部变量可以用类型相符的任意表达式来初始化,而全局 变量只能用常量表达式(Constant Expression)初始化。
• double pi = acos(-1.0);//不合法
最错误的例子:
#include<stdio.h>
double a = -1.0 ;
double pi = acos(a);//作全局变量,被编译器调用,编译器没有变量a,会出错,Error] initializer element is not constant
int main()
{
printf(“%.15lf\n”,pi);

return 0;
}
结果:
3 16 C:\Users\13712\Desktop\111.c [Warning] incompatible implicit declaration of built-in function ‘acos’(内部函数acos的隐式声明不兼容)
3 4 C:\Users\13712\Desktop\111.c [Error] initializer element is not constant(初始化元素不是一个常量)
改正步骤一:
#include<stdio.h>
double pi = acos(-1.0);//作全局变量,被编译器调用,可以调用系统内置函数,acos(-1.0)得到一个常数,可以在这里看作常量,但是实际不是;所以会有警告[Warning] initializer element is not a constant,而不是错误了 expression
int main()
{
printf(“%.15lf\n”,pi);

return 0;
}
编译结果:
2 16 C:\Users\13712\Desktop\111.c [Warning] incompatible implicit declaration of built-in function ‘acos’
2 16 C:\Users\13712\Desktop\111.c [Warning] initializer element is not a constant expression
运行结果:
3.141592653589793


Process exited after 0.02206 seconds with return value 0
请按任意键继续. . .

改正步骤二:
#include<stdio.h>
#include<math.h>//加上后会消除这个警告 [Warning] incompatible implicit declaration of built-in function ‘acos’
double pi = acos(-1.0);//acos(-1.0)得到一个常数,可以在这里看作常量,但是实际不是;所以会有警告[Warning] initializer element is not a constant,而不是错误了
int main()
{
printf(“%.15lf\n”,pi);

return 0;
}
编译结果:
3 16 C:\Users\13712\Desktop\111.c [Warning] initializer element is not a constant expression
运行结果:
3.141592653589793


Process exited after 0.03034 seconds with return value 0
请按任意键继续. . .

局部变量(Local Variable)

  1. 函数中定义的变量称为局部变量(Local Variable)
    2.局部变量的含义:  1、一个函数中定义的变量不能被另一个函数使用。 2、每次调用函数时局部变量都表示不同的存储空间。
    3.全局变量被编译器调用,局部变量被进程调用。
    #include<stdio.h>
    #include<math.h>

    int main()
    {
    double a = -1.0;

    double pi = acos(a);//局部变量被进程调用,可以用变量初始化

    printf(“%.15lf\n”,pi);

    return 0;
    }
    结果:
    3.141592653589793


Process exited after 0.02854 seconds with return value 0
请按任意键继续. . .

                                                                                                                              递归

递归—汉诺塔
#include <stdio.h>

void hano(int n , char src, char dest, char aux)
{

if (n == 1)
{
    printf("NO.%2d from %c to %c\n", n, src, dest);
    return;
}

hano(n - 1, src, aux, dest);
printf("NO.%2d from %c to %c\n", n, src, dest);  
hano(n - 1, aux, dest, src);
return;

}

int main()
{
hano(3, ‘A’, ‘C’, ‘B’);

return 0;

}

结果:
NO. 1 from A to C
NO. 2 from A to B
NO. 1 from C to B
NO. 3 from A to C
NO. 1 from B to A
NO. 2 from B to C
NO. 1 from A to C


Process exited after 0.1655 seconds with return value 0
请按任意键继续. . .

图示:
在这里插入图片描述

阶乘——递归
40!(迭代法)
#include<stdio.h>
#define FACT_LEN 60
int *get_one(int *total,int len,int n)
{

int carry = 0;
int i;
for(i = len-1;i >= 0;i--)
{
    total[i] = total[i]*n + carry;
    carry = total[i]/10;
    total[i] = total[i] % 10;
}
return total;

}
void get_fact(int *total,int len)
{
int n;
for(n = 1;n <= 40;n++)
{
get_one(total,len,n);
}

}
void printf_array(int*total,int len)
{
int i;
for(i=0;i < len - 1;i++)
{

 printf("%d",total[i]);
}

}
int main()

{
int total[FACT_LEN];
int len = FACT_LEN;
int i ;
for(i = 0;i < FACT_LEN;i++)
{
total[i] = 0;
}
total[59] = 1;
get_fact(total,len);
printf_array(total,len);
}
对比输出位置:
#include<stdio.h>
#define FACT_LEN 60
int *get_one(int *total,int len,int n)
{

int carry = 0;
int i;
for(i = len-1;i >= 0;i--)
{
    total[i] = total[i]*n + carry;
    carry = total[i]/10;
    total[i] = total[i] % 10;
}
return total;

}
void get_fact(int *total,int len)
{
int n;
for(n = 1;n <= 40;n++)
{
get_one(total,len,n);
printf_array(total,len);
printf(“\n”) ;
}

}
void printf_array(int*total,int len)
{
int i;
for(i=0;i < len - 1;i++)
{

 printf("%d",total[i]);
}

}
int main()

{
int total[FACT_LEN];
int len = FACT_LEN;
int i ;
for(i = 0;i < FACT_LEN;i++)
{
total[i] = 0;
}
total[59] = 1;
get_fact(total,len);

}
递归 ——阶乘
#include<stdio.h>
int factorial(int n)
{
if (n == 0)
{
return 1;
}
else
{
int recurse = factorial (n - 1);

    int result = n * recurse ;
    
    return result;
    
}

}
int main()
{
int n = 15;//n不能到40,否则结果太大了,也占用了太多栈空间,(是哪种原因导致的错误呢?)

int result = factorial(n) ;

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

return 0;

}
图示:
方法1:
在这里插入图片描述

简便图示:

在这里插入图片描述

方法二:
#include<stdio.h>
int factorial(int n)
{
if (n == 0)
{
return 1;
}
else
{
int result = n * factorial (n - 1);

    return result;
    
}

}
int main()
{
int n = 3;//n不能到40,否则结果太大了,也占用了太多栈空间,(是哪种原因导致的错误呢?)

int result = factorial(n) ;

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

return 0;

}
图示:在这里插入图片描述

简便图略
goto语句:
#include<stdio.h>
int main()
{
int a = 100;
int b = 200;

add1: a = a + b ;//标号:同变量 函数的命名规则一样,后面加一个冒号,一般顶格书写
add2: b = a - b ;
add3: a = a + b ;
if(a > 1000)
{
goto end;
}
goto add1;//直接跳到add1

end: printf(“%d %d\n”,a , b);

return 0;

}

结果:
2200 900


Process exited after 0.02789 seconds with return value 0
请按任意键继续. . .

                                                                           理解函数调用

函数的多次调用;结合递归理解;顺带理解一下return 和 exit:
#include <stdio.h>
#include <stdlib.h>

void fun4()
{
printf(“4 in\n”);
printf(“4 out\n”);
}

void fun3()
{
printf(“3 in\n”);
fun4();
printf(“3 out\n”);
}

void fun2()
{
printf(“2 in\n”);
fun3();
printf(“2 out\n”);
}

void fun1()
{
printf(“1 in\n”);
fun2();
printf(“1 out\n”);
}

int main()
{
fun1();
return 0;
}
结果:
1 in
2 in
3 in
4 in
4 out
3 out
2 out
1 out


Process exited after 0.02096 seconds with return value 0
请按任意键继续. . .
有exit()
#include <stdio.h>
#include <stdlib.h>

void fun4()
{
printf(“4 in\n”);
exit(1);
//return;
printf(“4 out\n”);
}

void fun3()
{
printf(“3 in\n”);
fun4();
printf(“3 out\n”);
}

void fun2()
{
printf(“2 in\n”);
fun3();
printf(“2 out\n”);
}

void fun1()
{
printf(“1 in\n”);
fun2();
printf(“1 out\n”);
}

int main()
{
fun1();
return 0;
}
1 in
2 in
3 in
4 in


Process exited after 0.03397 seconds with return value 1
请按任意键继续. . .
有 return:
#include <stdio.h>
#include <stdlib.h>

void fun4()
{
printf(“4 in\n”);
//exit(1);
return;
printf(“4 out\n”);
}

void fun3()
{
printf(“3 in\n”);
fun4();
printf(“3 out\n”);
}

void fun2()
{
printf(“2 in\n”);
fun3();
printf(“2 out\n”);
}

void fun1()
{
printf(“1 in\n”);
fun2();
printf(“1 out\n”);
}

int main()
{
fun1();
return 0;
}
结果:
1 in
2 in
3 in
4 in
3 out
2 out
1 out


Process exited after 0.02223 seconds with return value 0
请按任意键继续. . .

exit是退出整个程序;return是返回调用函数的地方;
二维数组的转制——三行两列用两行三列表出
#include<stdio.h>
void reverse_my(int *m)
{

int i;
int j;
int temp;
int col = 3;
temp = *(m + col * 0 + 1);
*(m + col * 0 + 1) = *(m + col * 1 + 0) ;
*(m + col * 1 + 0) = temp;

temp = *(m + col * 0 + 2);
*(m + col * 0 + 2) = *(m + col * 1 + 0) ;
*(m + col * 1 + 0) = temp;
    

temp = *(m + col * 1 + 0);
*(m + col * 1 + 0) = *(m + col * 1 + 1) ;
*(m + col * 1 + 1) = temp;    

// for(i = 0;i < 2; i++)//我认为在这个子函数中;数组元素均是指针;这样写是在输出指针数组;即一串地址,而不是主函数中的数组的内容;为什么数既不是数组也不是指针,还不是向量呢?
// {
// for(j = 0;j < 3 ; j++)
// {
// printf(“%P\n”,m[i][j]);//这里的m表示一维数组的首地址,子函数不存在二维数组
// }
// }
for(i = 0;i < 6; i++) //这样可以输出你的数组内容
{

       printf("%4d",*(m + i ));//m是一维数组首地址
       
   }
    printf("\n");
        for(i = 0;i < 6; i++) //这样可以输出你的数组内容
     {
        
       printf("%4d",m[i]);//m是一维数组首地址 ,数组元素均是指针
       
   }
    printf("\n");

}

int main()
{
int m[2][3] = {1,2,3,4,5,6};
int i;
int j;
int xunhuancishu = 0;
reverse_my((int*)m);/这里的m是什么?(二维数组指针强制转换成一维数组指针)我感觉是是加了int后,若形参中的m+1,则是在地址m的基础上加一个int空间后的地址,若不加,m+1 则是在地址m的基础上加一个数组空间后的地址*/
for(i = 0;i < 2; i++)
{
for(j = 0;j < 3 ; j++)
{
xunhuancishu++;
printf(“%4d”,m[i][j]);
if(xunhuancishu % 2 == 0)
{
printf(“\n”);
}

     }
    
   }

}
结果:
1 4 2 5 3 6
1 4 2 5 3 6
1 4
2 5
3 6


Process exited after 0.02274 seconds with return value 10
请按任意键继续. . .

strlen 与 sizeof 的区别:
#include<stdio.h>
int fun (char a[20])
{
printf(“%d\n”,strlen(a));
printf(“%d\n”,sizeof(a));
return 0;
}
int main()
{

char a[20]="123456";
char *p="123456";
printf("%d\n",sizeof(int));
printf("%d\n",strlen(a));
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(p));
printf("%d\n",sizeof("123456"));//测容器大小
printf("%d\n",strlen("123456"));//测内容大小,测字符串长度,不带/0
fun(a);
fun(p);

}
结果:
4
6
20
8
7
6
6
8
6
8


Process exited after 0.06622 seconds with return value 0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值