1 通过指针自己实现strcat函数的功能
#include<stdio.h>
#include<string.h>
int main(int argc, const char *argv[])
{
char s1[100]="hello";
char s2[100]="abcde";
printf("s1:");puts(s1);
printf("s2:");puts(s2);
int lens1=strlen(s1);
int lens2=strlen(s2);
char *ps1=&s1[lens1];
char *ps2=s2;
int i;
for(i=0;i<lens2;i++,ps1++,ps2++){
*ps1=*ps2;
}
printf("strcat后:");puts(s1);
return 0;
}
2 实现atoi函数的功能 “123456” --> 123456
#include<stdio.h>
#include<string.h>
int main(int argc, const char *argv[])
{
char s[]="123456";
int num=0;
char *p=s;
int len=strlen(s);
while(*p){
num=num*10+*p-'0';
p++;
}
printf("字符串为:");puts(s);
printf("数字为:%d\n",num);
return 0;
}
3 输入一个字符串,输出字符串中有多少个空格
#include<stdio.h>
#include<string.h>
int main(int argc, const char *argv[])
{
char s[32]={};
printf("请输入一个字符串:\n");
gets(s);
char *p=s;
int num=0;
while(*p!='\0'){
if(*p==' ')
num++;
p++;
}
printf("字符串中有 %d 个空格\n",num);
return 0;
}
4 输入一个字符串,将所有的大写字母转化为小写字母,小写字母转化为大写字母,所有的数字型字符转化为-,其它字符用*表示。
#include<stdio.h>
#include<string.h>
int main(int argc, const char *argv[])
{
char s[100]={};
printf("请输入一个字符串:\n");
gets(s);
//scanf("%s",s);
char *p=s;
while(*p){
if(*p>='A'&&*p<='Z')
*p+=32;
else if(*p>='a'&&*p<='z')
*p-=32;
else if(*p>='0'&&*p<='9')
*p='-';
else
*p='*';
p++;
}
printf("转化后的字符串:%s\n",s);
return 0;
}
1 不同类型变量与 0 值比较
整型:if(a==0) or if(a!=0)
浮点型:if(x==0.0) 要避免写成== !=,应设法转化成>= <=
指针变量:if(p==NULL) or if(p!=NULL)
2 野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)指针变量在定义时如果未初始化,其值是随机的,指针变量的值是别的变量的地址,意味着指针指向了一个地址是不确定的变量,此时去解引用就是去访问了一个不确定的地址,所以结果是不可知的。
3 空指针
指针的值不能时整型值,但空指针是个例外,空指针的值可以是一个纯粹的0。空指针的值为NULL,NULL是在‘stddef.h‘中定义的一个宏,它的值和任何有效指针的值都不同。NULL是一个纯粹的0,可能会被强制转换成’void*‘或’char*‘类型。
4 万能指针
void指针一般被称为通用指针或泛指针,它是c语言关于“纯粹地址”的一种约定。void指针指向某个对象,但该对象不属于任何类型。
int *ip;
void *p;
ip指向一个整型值,而p指向的值不属于任何类型。
可以用其他类型的指针来代替void指针,或者用void指针代替其他类型的指针,并且不需要进行强制转换。
5 常见的字符串处理函数
使用字符串处理函数,必须加头文件<string.h>
strcat(char *dest,char *src)
将源串src添加到目标串dest后面,并在得到的新串后面加上结束标记。
strcpy(char *dest,char *src)
复制源串src到目标串dest所指定的位置,包含结束标记。
int strcmp(char *s1,char *s2)
比较字符串s1和s2的大小
返回值<0,表示s1<s2;
返回值=0,表示s1=s2;
返回值>0,表示s1>s2;
6 指针的算数运算
+:p + n 指针向后偏移n个操作空间
-:p - n 指针向前偏移n个操作空间
++:p++ 指针向后偏移1个操作空间(改变了p的指向)
–:p-- 指针向前偏移1个操作空间(改变了p的指向)
px - py: 两个指针相减,表示两个地址空间之间元素的个数(px和py指针的类型必须相同,否则没有意义) 注意:指针只能进行相减的运算,(+ * /)都没有意义。
int arr[6] = {10,20,30,40,50,60};
int *p1 = &arr[0];
int y = *p1; //y=10
y = ++*p1;
printf("y = %d,*p1 = %d\n",y,*p1); //y=11,*p1=11
y = *p1++; //先*p1结合赋值给y,然后指针p1++;
printf("y = %d,*p1 = %d\n",y,*p1); //y=10,*p1=20
y = (*p1)++;
printf("y = %d,*p1 = %d\n",y,*p1); //y=20,*p1=21
7 指针占几个字节,为什么?
一个指针在32位的计算机上,占4个字节; 一个指针在64位的计算机上,占8个字节。
指针就是地址,地址就是指针,而地址是内存单元的编号。所以,一个指针占几个字节,等于是一个地址的内存单元编号有多长。
我们一般需要32个0或1的组合就可以找到内存中所有的地址,而32个0或1的组合,就是32个位,也就是4个字节的大小,因此,我们只需要4个字节就可以找到所有的数据。所以,在32位的计算机中,指针占4个字节
8 指针常量与常量指针
int * const p //指针常量:指针在前,常量在后
这个p只能指向一个位置,而不能指向其他位置。指向的变量值可以改变。
const int *p = &a; //常量指针 :常量在前,指针在后
指向的变量值不能改变,但是可以改变这个指针指向的位置。
9 动态分配内存
大多数情况下,可以看到程序使用的内存是通过显式声明分配给变量的内存(也就是静态内存分配)。
这一点对于节省计算机内存是有帮助的,因为计算机可以提前为需要的变量分配内存。
但是在很多应用场合中,可能程序运行时不清楚到底需要多少内存,
这时候可以使用指针,让程序在运行时获得新的内存空间(实际上应该就是动态内存分配),
并让指针指向这一内存更为方便。
10 库函数的使用方式有:
1.静态链接:静态链接是指系统在链接阶段把程序的目标文件和所需的函数库文件链接在一起,由此生成的可执行文件可以在没有函数库的情况下运行。
2.动态链接:动态链接是指系统在链接阶段并没有把目标文件和函数库文件链接在一起,程序在运行过程中需要使用函数库中的函数时才链接函数库。
比较:
使用静态链接方式产生的可执行文件比较大,但运行效率较高;使用动态链接方式产生的可执行文件比较小,
但由于需要动态加载函数库,所以运行效率会低一点。特别要注意一点,在使用动态链接时,
需要同时将函数库复制到将要运行程序(使用动态链接生成的可执行文件)的计算机中。
如果将要运行程序的计算机中没有对应的函数库,那么程序是无法运行的。
一 循环语句
1.1 while循环
格式:
while(表达式)
{
语句块;
}
注意:先判断表达式是否为真,如果为真,则执行语句块,执行结束后,再次判断表达式是否为真,为真则执行语句块,直到表达式为假,跳出while循环。
循环条件不能永远为真,必须要有循环结束的条件,如果永远循环,则称之为死循环。
#include <stdio.h>
int main(int argc, const char *argv[])
{
//程序员 40万,房价200万,10%,如果不涨工资,不贷款,问:几年可以买房
int sum,year = 0;
double price = 200;
while(1)
{
year++;
sum += 40;
price = price*1.1;
if(sum >= price)
{
printf("第%d年买房!\n",year);
break;
}
printf("第%d年买不起房!\n",year);
}
return 0;
}
练习1:求1到100的和
int sum = 0;
int i = 1;
while(i <= 100)
{
sum += i;
i++;
}
printf("sum = %d\n",sum);
1.2 do…while…
格式:
do
{
语句块
}while(表达式)
#include <stdio.h>
int main(int argc, const char *argv[])
{
/*
while(0)
{
printf("hello world!\n");
}*/
do{ //先执行一次代码块,再判断条件是成立
printf("hello world!\n");
}while(0);
return 0;
}
1.3 for循环
格式:
for(表达式1;表达式2;表达式3)
{
语句块;
}
注意:
1.表达式1主要用于对计数的变量进行赋初值,整个循环只会执行一次,且是在第一次的时候执行,也可以省略不写。
2,不管省略哪个表达式,分号不能省略for(;;) -->while(1);
3.表达式2主要是判断条件,条件为真,执行语句块,条件为假,则结束循环,如果表达式2一直为真,则为死循环
for(int i = 0;;i++);
4.表达式3主要就是为了改变计数变量的值从而影响循环的次数,可以省略放到语句块中执行,通常不省略。
#include <stdio.h>
int main(int argc, const char *argv[])
{
int i,j;
/*for(i = 0; i <= 5;i++)
{
if(1 == i)
{
//break;结束本层循环
continue; //结束本次循环,进入下一次循环
}
printf("hello world!\n");
}*/
for(i = 0 ; i < 3;i++)
{
for(j = 0 ;j < 4;j++)
{
if(j == 2)
{
//break;
continue;
}
printf("hello world! i = %d j = %d\n",i,j);
}
}
return 0;
}
练习:求1000以内的所有完数
完数:完美的数字,一个数的所有的因子,除去它本身相加还等于这个数,则这个数称为完数
6的因子:1 2 3 6
1 + 2 + 3 = 6
要求使用for循环实现,打印出所有的完数?
#include <stdio.h>
int main(int argc, const char *argv[])
{
int sum = 0;
//int i = 0;
for(int i = 1;i < 1000;i++)
{
sum = 0;
for(int j = 1 ; j < i;j++)
{
if(i % j == 0)
{
sum += j;
}
}
if(sum == i)
{
printf("%d ",i);
}
}
putchar(10);
return 0;
}
二 辅助控制关键词
2.1 goto
goto主要用于在一个函数内进行跳转,可以跳转到当前函数的任意位置。
不建议过多使用,可读性,逻辑性变差。
#include <stdio.h>
int main(int argc, const char *argv[])
{
/*
int num = 100;
goto NEXT;
num = 888;
NEXT:
printf("hello world!\n");
printf("num = %d\n",num);
*/
int i = 0 ;
//使用goto语句实现一个循环
TEMP:
i++;
if(i < 9)
{
printf("hello world!\n");
goto TEMP;
}
return 0;
}
2.2 break
break 在switch语句中主要用于结束指定的语句块
break主要用在循环语句中,功能是结束本层循环。
注意:break除了在switch语句中以外,只能在循环语句中使用
2.3 continue
continue只能用在循环语句中,表示结束本次循环,直接进入下一次循环。
2.4 return
主要用于结束当前函数,立即返回到它的调用处。
return 语句可以出现在函数的任意位置。
三 数组的概念
数组:保存一组相同类型的数据
不管是几维数组都是开辟一段连续的内存空间
数组是一种构造数据类型(构造数据类型:数组,结构体,联合体等)
3.1 一维数组
格式:
<存储类型> <数据类型> 数组名[数组下标]
存储类型:auto,register,static,extern,不写默认是auto
数据类型:基本数据类型:char,short,int等
数组名:遵从标识符的命名规则。
数组下标:确定的是元素的个数
例如:
int a[10]
含义:定义一个名为a的数组,一共10个元素,每个元素的类型都是Int类型
3.2 一维数组的性质
#include <stdio.h>
int main(int argc, const char *argv[])
{
int a[4]; //定义一个一维数组
a[0] = 100;
a[1] = 200;
a[2] = 300;
a[3] = 400;
//a[4] = 500;使用的时候,下标范围为0~3
printf("%d %d %d %d\n",a[0],a[1],a[2],a[3]);
printf("%p\n",a); //数组首元素的地址
printf("%p\n",&a[0]); //数组首元素的地址
printf("%p\n",&a); //数组的地址
printf("**************\n");
printf("%p\n",a+1); //数组首元素的地址
printf("%p\n",&a[0]+1); //数组首元素的地址
printf("%p\n",&a+1); //数组的地址
//a++; //a = a+1 数组名相当于是常指针,不能被修改
return 0;
}
3.3 一维数组的初始化和遍历
初始化:在定义的同时赋值
#include <stdio.h>
int main(int argc, const char *argv[])
{
//int a[5]; //未初始化
//int a[5] = {1,2,3,4,5}; //全部初始化
//int a[5] = {1,2,3}; //部分初始化,会自动将未设置的值设置为0
//int a[5] = {0}; //将所有的元素全部初始化为0
//a = {1,2,3,4,5}; //错误,不能整体赋值
//int a[5] = {}; //不推荐此写法,有些编译器不支持
int a[] = {1,2,3,4,5,6}; //会根据实际使用的大小给数组a开辟空间(用多少给多少)
int length = sizeof(a)/sizeof(a[0]);
for(int i = 0 ;i < length;i++)
{
printf("%d ",a[i]);
}
putchar(10);
return 0;
}
3.4 冒泡排序法
基本思想:以从小到达排序为例:
先进行一趟排序,从起始位置开始,两两元素进行比较,将较大值交换后后面,一趟排序结束后,最大值就被交换到最后面了。
进行第二趟排序,将次大值放置在倒数第二个位置,重复以上步骤,最终得到一个有序的序列。
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
srand(time(NULL));
int a[10000] = {0};
for(int i = 0 ; i < 10000;i++)
{
a[i] = rand() % 1000;
}
//int a[] = {9,8,4,3,2,1,3,5,6,0,5,4,3,2,1};
int length = sizeof(a)/sizeof(a[0]);
for(int i = 0;i < length -1;i++)
{
for(int j = 0 ; j < length -i-1;j++)
{
if(a[j] < a[j + 1])
{
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
for(int i = 0; i < length;i++)
{
printf("%d ",a[i]);
}
putchar(10);
return 0;
}
四 二维数组
4.1 二维数组的定义和性质
格式:
例如:int a[5][6];
//定义一个名为a的二维数组,一共30个元素,每个元素都是int类型
#include <stdio.h>
int main(int argc, const char *argv[])
{
//int a[2][3]; //定义一个两行三列的二维数组
int a[2][3] = {{1,2,3},{4,5,6}};
//int a[2][3] ={{1,2},{4}};
//int a[][3] = {{1,2,3},{4,5}}; //按列存储
//int a[2][] = {{1,2,3},{4,5}}; // 错误,c语言不支持按行存储
//int a[2][3] = {1,2,3,4,5,6};
//int a[2][3] = {0}; //将所有的元素全部初始化为0
int i,j;
for(i = 0 ; i < 2;i++)
{
for(j = 0 ; j < 3;j++)
{
printf("%-5d",a[i][j]);
}
putchar(10);
}
printf("&a[0][0] %p\n",&a[0][0]);//表示的是首行首元素的地址
printf("&a[0] %p\n",&a[0]); //表示行地址
printf("a[0] %p\n",a[0]); //表示此行首元素的地址
printf("a %p\n",a); //二维数组的数组名表示的是行地址
printf("&a %p\n",&a); //表示二维数组的地址,操作空间就是整个二维数组
printf("*********************\n");
printf("&a[0][0]+1 %p\n",&a[0][0]+1);//表示的是首行首元素的地址
printf("&a[0]+1 %p\n",&a[0]+1); //表示行地址
printf("a[0]+1 %p\n",a[0]+1); //表示此行首元素的地址
printf("a+1 %p\n",a+1); //二维数组的数组名表示的是行地址
printf("&a+1 %p\n",&a+1); //表示二维数组的地址,操作空间就是整个二维数组
return 0;
}
五 字符数组和字符串
字符数组:数组中每一个元素都是一个字符
#include <stdio.h>
int main(int argc, const char *argv[])
{
char ch1[] = {'h','e','\0','l','o','\0'}; //字符数组
char ch2[] = "hello"; //也是字符数组(多一个\0)
//如何遍历?
//ch1:
int lenth = sizeof(ch1)/sizeof(ch1[0]);
for(int i = 0 ; i < lenth;i++)
{
printf("%c ",ch1[i]);
}
putchar(10);
//ch2:
int lenth2 = sizeof(ch2)/sizeof(ch2[0]);
for(int i = 0 ; i < lenth2;i++)
{
printf("%c [%d] ",ch2[i],ch2[i]);
}
putchar(10);
printf("ch1 = %s\n",ch1);
printf("ch2 = %s\n",ch2);
printf("&ch1 = %p\n",ch1);
printf("&ch2 = %p\n",ch2);
char ch3[] = "hello\0world";
printf("sizeof(ch3) = %ld\n",sizeof(ch3));
//二维字符数组
char ch4[4][32] = {"helloworld","nihaobeijing","hellokitty","www.baidu.com"};
int i,j;
for(i = 0 ; i < 4;i++)
{
for(j = 0 ; j < 32;j++)
{
printf("%c",ch4[i][j]);
}
putchar(10);
}
return 0;
}
5.1 字符串的逆序
char ch[] ="helloworld";
....
ch:dlrowolleh
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
char ch[] = "helloworld";
int len = 0;
int i = 0;
while(ch[i] != '\0')
{
len++;
i++;
}
//int len = strlen(ch);
int j = len -1;
for(int i = 0 ; i < len /2;i++,j--)
{
char t = ch[i];
ch[i] = ch[j];
ch[j] = t;
}
printf("%s\n",ch);
return 0;
}
练习:插入数据
从终端获取字符串,插入的位置,以及插入的元素
输出插入的结果
helloworld 4 x
hellxoworld
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
char str[32] ={0};
char ch;
int num;
printf("请输入一个字符串:\n");
scanf("%s",str);
printf("请输入要插入的字符:\n");
scanf("%*c%c",&ch);
printf("请输入要插入的位置:\n");
scanf("%d",&num);
int len = strlen(str);
for(int i = 0 ; i < len - num;i++)
{
str[len-i] = str[len-1-i];
}
str[num] = ch;
printf("str = %s\n",str);
return 0;
}
六 字符串函数
6.1 为什么要使用字符串函数
一般字符串都是放在一个数组中,但是数组定义好之后,不能整体操作,所以我们需要借助字符串相关操作的函数。
#include <stdio.h>
int main(int argc, const char *argv[])
{
char ch1[] = "helloworld";
char ch2[] = "helloworld";
if(ch1 == ch2)
{
printf("ch1 = ch2\n");
}
else
{
printf("ch1 != ch2\n");
}
return 0;
}
6.2 常用的字符串处理函数
(1) strlen
#include <string.h>
size_t strlen(const char *s);
功能:获取一个字符串的长度
参数:
s:要获取长度的字符串
直接传一个字符串或者字符数组名都可以
返回值:
字符串的长度
注意:所有使用strlen获取的字符串,都必须要有\0,
因为strlen遇到第一个\0立即结束读取,并且获取的长度不包括\0
作业:
1.总结sizeof和strlen的区别?
1.sizeof是关键词,strlen是函数
2.sizeof计算是数据类型所占内存空间的大小,strlen计算的是字符串的长度(不带\0)
char ch[] = "helloworld";
3.sizeof是在编译阶段计算内存空间的大小,strlen是在运行时进行计算。
2.自己实现strlen函数
#include <stdio.h>
int main(int argc, const char *argv[])
{
char str[32] = {0};
printf("请输入一个字符串:\n");
scanf("%s",str);
/*int i = 0,lenth = 0;
while(str[i] != '\0')
{
lenth++;
i++;
}*/
int i,lenth;
for(i = 0,lenth = 0; str[i]!= '\0';i++)
{
lenth++;
}
printf("%s的长度为%d\n",str,lenth);
return 0;
}