Day4 C语言基础语法学习-3
一 数组
1.1 数组的概念
数组:
保存一组相同类型的数据
不管是几维数组,都是开辟一段连续的内存空间
数组是一种构造数据类型(数组,结构体,共用体)
1.2 一维数组
1.2.1 一维数组的定义
<存储类型> <数据类型> <数组名> [数组下标]
存储类型:auto,register,static,extern
数据类型:基本数据类型:int,char,float…等
数组名:是一个标识符,满足标识符的命名规则
数组下标:确定数组张元素的个数
例如:
int a[10];
含义:定义一个名为a的数组,一共10个元素,每个元素都是int类型。
1.2.2 一维数组的性质
#include <stdio.h>
int main(int argc, const char *argv[])
{
int a[4];
a[0] = 222;
a[1] = 333;
a[2] = 444;
a[3] = 555;
printf("%d %d %d\n",a[0],a[1],a[2]);
//数组在定义和使用的时候,数组下表尽量不要使用变量
//防止后期变量改变会影响对数组的操作,一般数组下标都是常量或者常量表单大师,宏定义本身也是一个常量表达式,所以可以当做数组下标使用
#if 0
int num = 10;
int b[num];
b[2] = 5;
int n =3;
b[n] = 888;
printf("%d %d\n",b[2],b[n]);
#endif
printf("sizeof(a) = %ld %ld\n",sizeof(a),sizeof(int)*4);
printf("%p\n",&a[0]);
printf("%p\n",&a[0]+1);
printf("%p\n",a);
printf("%p\n",a+1);
printf("%p\n",&a);
printf("%p\n",&a+1);
a++; //数组名是常指针,不能修改
return 0;
}
1.3 一维数组的初始化和遍历
1.全部初始化
2.局部初始化
3.全部初始化不指定数组下标
#include <stdio.h>
int main(int argc, const char *argv[])
{
//如果在函数内部定义一个数组没有初始`在这里插入代码片`化,那么每一个元素都是随机值
//int a[5];
//a = {1,2,3,4,5}; //错误写法
//全部初始化
//int a[5] = {1,2,3,4,5};
//int a[5] = {0}; //全部初始化为0
//int a[5] = {}; //有的编译器不支持此种写法
//int a[5] = {1,2,3}; //局部初始化,没有赋值的元素自动初始化为0
int a[] = {1,2,3,4,5,6,7}; //不指定数组下标,系统会根据初始化的数据的个数设置数组下标
printf("sizeof(a) = %ld\n",sizeof(a));
/*一维数组的遍历*/
int i;
for(i = 0 ; i < sizeof(a)/sizeof(a[0]);i++)
{
printf("%d ",a[i]);
//printf("a[%d] = %d\n",i,a[i]);
}
putchar(10);
return 0;
}
1.4 冒泡排序法
#include <stdio.h>
int main(int argc, const char *argv[])
{
int a[10] = {0};
printf("请输入10个数字:\n");
int i,j;
int length = sizeof(a)/sizeof(a[0]);
for(i = 0 ; i < length;i++)
{
scanf("%d",&a[i]);
}
for(i = 0 ; i < length - 1;i++)
{
for(j = 0 ; j < length - 1 - i;j++)
{
if(a[j] < a[j+1])
{
#if 0
//数据交换,方式1
int t = a[j];
a[j] = a[j+1];
a[j+1] = t;
#endif
//数据交换,方式2
a[j] = a[j] + a[j + 1];
a[j + 1] = a[j] - a[j + 1];
a[j] = a[j] - a[j + 1];
}
}
}
for(i = 0 ; i < length;i++)
{
printf("%d ",a[i]);
}
putchar(10);
return 0;
}
二、二维数组
2.1 二维数组的定义和性质
书写方式:
存储类型 数据类型 数组名 [行数][列数];
例如:
int arr[3][4];
#include <stdio.h>
int main(int argc, const char *argv[])
{
int a[2][3] = {{1,2,3},{4,5,6}};
//int a[2][3] = {{1},{4,5}};
//int a[][3] = {{1,2,3},{4,5,6}};
//int a[][3] = {1,2,3,4,5,6,7,8};
//int a[2][3] = {0};
int i,j;
for(i = 0;i < 3;i++)
{
for(j =0 ; j < 3;j++)
{
printf("a[%d][%d] = %d\n",i,j,a[i][j]);
}
}
printf("%p\n",&a[0][0]);
printf("%p\n",&a[0]);
printf("%p\n",a);
printf("%p\n",&a);
printf("%p\n",&a[0][0] + 1);
printf("%p\n",&a[0] + 1);
printf("%p\n",a + 1);
printf("%p\n",&a + 1);
return 0;
}
2.2 二维数组的初始化和遍历
#include <stdio.h>
int main(int argc, const char *argv[])
{
//int a[3][4];
//int a[2][3] = {{4,5,6},{7,8,9}};//全部初始化
//int a[2][3] = {1,2,3,4}; //按行存储,没有设置的自动补0
//int a[2][3] = {{1},{2}};
//int a[][3] = {{10,20},{30}};
//int a[2][] = {1,2,3,4,5}; //错误写法
int i,j;
//外层循环控制行数
//内层循环控制列数
for(i = 0 ; i < 2;i++)
{
for(j = 0 ; j < 3;j++)
{
printf("%-5d",a[i][j]);
}
putchar(10);
}
return 0;
}
三、字符数组和字符串
字符数组:数组里面保存的每一个元素都是字符
字符串本质也是一个字符数组
#include <stdio.h>
int main(int argc, const char *argv[])
{
char ch1[] = {'h','e','l','l','o'};
printf("sizeof(ch1) = %ld\n",sizeof(ch1));
//字符数组的遍历
int i;
for(i = 0; i < sizeof(ch1)/sizeof(ch1[0]);i++)
{
printf("%c ",ch1[i]);
}
putchar(10);
char ch2[] = "world";
printf("sizeof(ch2) = %ld\n",sizeof(ch2));
printf("ch2 = %s\n",ch2);
char ch3[] = {'h','e','l','l','o','\0'};
printf("ch3 = %s\n",ch3);
char ch4[] = "hello\0world";
printf("sizeof(ch4) = %ld\n",sizeof(ch4));
for(i = 0 ; i < sizeof(ch4)/sizeof(ch4[0]);i++)
{
printf("[%c] %d\n",ch4[i],ch4[i]);
}
puts("---------------------------------------");
char str[4][32] = {"hello","nihao beijing","hello kitty","welcome to nanjing"};
int j;
for(i = 0 ; i < 4;i++)
{
for(j = 0 ; j < 32;j++)
{
printf("%c",str[i][j]);
}
putchar(10);
}
return 0;
}
3.1 字符串逆序
#include <stdio.h>
int main(int argc, const char *argv[])
{
char str[32] = {0};
printf("请输入一个字符串: ");
scanf("%s",str);
int i = 0,length = 0;
while(str[i] != '\0')
{
length++;
i++;
}
int x = 0, y = length -1;
for(i = 0 ; i < length /2 ;i++)
{
char t = str[x];
str[x] = str[y];
str[y] = t;
x++;
y--;
}
printf("%s\n",str);
return 0;
}
3.2 插入数据
输入一个字符串,位置,插入的元素
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
char str[32] = {0};
int num,i;
char ch;
printf("请输入字符串,位置,插入的元素:\n");
scanf("%s%d %c",str,&num,&ch);
int length = strlen(str);
for(i =0 ; i < length - num +1;i++)
{
str[length - i] = str[length -i - 1];
}
str[num - 1] = ch;
printf("%s\n",str);
return 0;
}
四 字符串函数
4.1 为什么要使用字符串函数
一般字符串都是保存在一个数组里面,但是数组定义好之后,是不能整体操作的,所以我们需要借助一下字符串相关操作的函数来对字符串进行操作
#include <stdio.h>
int main(int argc, const char *argv[])
{
char s1[] = "hello world";
char s2[] = "hello world";
if(s1 == s2)
{
printf("s1 = s2\n");
}
else
{
printf("s1 != s2\n");
}
//数组如果没有初始化,数组下标必须写
//char str[];
return 0;
}
4.2 常用字符串函数
4.2.1 strlen()
头文件:#include <string.h>
原型:size_t strlen(const char *s);
功能:获取一个字符串的长度
参数:s:要获取长度的字符串
直接传入一个字符串,或者字符数组名都可以
返回值:字符串的长度
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
//strlen函数获取字符串的长度
//获取的长度是这个字符串中第一\0位置之前的长度,不包括\0
char s1[] = "hello world";
printf("strlen(s1) =%ld\n",strlen(s1));
printf("sizeof(s1) =%ld\n",sizeof(s1));
char s2[] = "hello wor\0ld";
printf("strlen(s2) =%ld\n",strlen(s2));
printf("sizeof(s2) =%ld\n",sizeof(s2));
//以下这个不是一个字符串,没有\0,所以不能用strlen来获取字符串长度,strlen会一直从首地址的位置找\0
char s3[] = {'h','e','l','l','o'};
printf("strlen(s3) =%ld\n",strlen(s3));
printf("sizeof(s3) =%ld\n",sizeof(s3));
char s4[32] = "hello world";
printf("strlen(s4) =%ld\n",strlen(s4));
printf("sizeof(s4) =%ld\n",sizeof(s4));
return 0;
}
4.2.2 strcmp()
头文件:#include <string.h>
原型:int strcmp(const char *s1, const char *s2);
功能: 比较两个字符串的大小
参数:s1,s2两个字符串
返回值:
0: s1 = s2
<0: s1 < s2
>0: s1 > s2
int strncmp(const char *s1, const char *s2, size_t n);
用于比较两个字符串前n个字节是否一样
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
//strcmp比较的是\0之前的内容,跟字符串所在内存空间没有关系
//char s1[32] = "hello w\0orld";
//char s2[] = "hello w\0orld";
char s1[] = "h";
char s2[] = "hello abcdefgwhi";
int ret = strcmp(s1,s2);
if(ret == 0)
{
printf("s1 = s2\n");
}
else if(ret > 0)
{
printf("s1 > s2\n");
}
else
{
printf("s1 < s2\n");
}
int k = strncmp(s1,s2,2);
if(k == 0)
{
printf("s1 = s2\n");
}
else if(k > 0)
{
printf("s1 > s2\n");
}
else
{
printf("s1 < s2\n");
}
return 0;
}
4.2.3 strcpy()
头文件:#include <string.h>
原型:char *strcpy(char *dest, const char *src);
功能: 将src字符串赋值到dest字符串中
参数:
dest:目的字符串
src:源字符串
返回值:
返回目的字符串的首地址
char *strncpy(char *dest, const char *src, size_t n);
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
char s1[32];
strcpy(s1,"hello world");
printf("s1 = %s\n",s1);
char s2[] = "hello world";
char s3[32] = "abcdefg";
//strcpy将 s3中的第一个\0复制给了s2
strcpy(s2,s3);
printf("sizeof(s2) = %ld\n",sizeof(s2));
printf("s2 = %s\n",s2);
int i;
for(i = 0 ; i < sizeof(s2)/sizeof(s2[0]);i++)
{
printf("[%c] %d\n",s2[i],s2[i]);
}
puts("------------------------");
char buf1[32] = "hello world";
char buf2[32] = "abcdefghijklmnopqrsst";
//strcpy(buf1,buf2);
//是将第二个参数的前n个字节复制给第一个参数
strncpy(buf1,buf2,7);
printf("buf1 = %s\n",buf1);
return 0;
}
4.2.4 strcat()
头文件:#include <string.h>
原型:char *strcat(char *dest, const char *src);
功能: 将src追加到dest的后面
参数:
dest:目的字符串
src:源字符串
返回值:
追加后字符串的首地址
char *strncat(char *dest, const char *src, size_t n);
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
char s1[32] ="hello wo\0rldadadjajdalsdkjaskdj"; //hello woabcdefg\0
char s2[32] ="abcdefg\0higk";
strcat(s1,s2);
printf("s1 = %s\n",s1);
return 0;
}
4.2.5 自己实现strcpy函数的功能
#include <stdio.h>
int main(int argc, const char *argv[])
{
char s1[32] = "welcome to nanjing";
char s2[32] = "hello world";
int i = 0;
while(s2[i] != '\0')
{
s1[i] = s2[i];
i++;
}
s1[i] = s2[i];
printf("s1 = %s\n",s1);
return 0;
}
五、指针概念
5.1 指针的用途
使用程序简洁,紧凑,高效
有效地表示复杂的数据结构
动态分配内存
得到多于一个函数的返回值
5.2 指针的概念
当程序中定义一个变量之后,程序就会为这个变量在内存中开辟内存空间,我们内存中每一个字节的空间都有一个编号,将这个编号称之为地址,地址也叫做指针
在不影响理解的情况,有时对地址,指针,指针变量不区分,通称指针
#include <stdio.h>
int main(int argc, const char *argv[])
{
//int a = 1;
//int *pa = &a;
printf("int* = %ld\n",sizeof(int *));
printf("char* = %ld\n",sizeof(char *));
printf("double* = %ld\n",sizeof(double *));
printf("float* = %ld\n",sizeof(float *));
printf("long* = %ld\n",sizeof(long *));
int a = 1;
char ch = 'a';
//int *pc = &ch; //类型不兼容
char *pch = &ch;
//ch = 'x';
*pch = 'x'; //等价于ch = 'x'
printf("ch = %c\n",ch);
printf("ch = %c\n",*pch);
int *pa = &a;
printf("%p\n",pch);
printf("%p\n",pa);
printf("%p\n",pch + 1); //不同类型的指针,步长不一样
printf("%p\n",pa + 1);
return 0;
}
5.2指针变量的运算
指针运算是以指针变量所存放的地址量作为运算量而进行运算的。
因此,指针运算的实质就是地址的运算。
指针运算的种类是有限的,它只能进行算术运算,关系运算和赋值运算。
5.2.1 算术运算符
+: px + n 指针向地址大的方向移动n个数据
-: px - n 指针向地址小的方向移动n个数据
++: px++ 指针向地址大的方向移动1个数据
–: px-- 指针向地址小的方向移动1个数据
指针变量加减,表示指针变量向地址大或小的方向移动N个操作空间,两个指针变量相减,表示两个地址之间有多少个操作空间(多少个元素)
【注意】
两个指针做运算,必须是同类型的,类型不同没有任何意义。
一个指针变量加减有意义,乘除没有意义
后置++优先级高于前置++
后置++结合律从左往右
前置++和*优先级相同,结合律从右往左
#include <stdio.h>
int main(int argc, const char *argv[])
{
int arr[6] = {1,3,5,8,9,10};
int *p1 = arr;
printf("*p1 = %d\n",*p1);
p1++;
int *p2 = p1++;
printf("*p1 = %d,*p2 = %d\n",*p1,*p2);
printf("p1 - p2 = %ld\n",p1 - p2);
int y = *p1; //取值
printf("y = %d\n",y);
y = ++*p1;
printf("y = %d\n",y);
y = (*p1)++;
printf("y = %d\n",y);
y = *p1++; //赋值完成后,指针px加1
printf("y = %d\n",y);
printf("*p1 = %d\n",*p1);
/*y = ++*p1++; //赋值完成后,指针px加1
printf("y = %d\n",y);
printf("*p1 = %d\n",*p1);
*/
return 0;
}
5.2.2 关系运算符
> px > py
<
>=
<=
!=
==
两个指针变量可以通过关系运算符来判断保存地址的大小;
两指针之间的关系运算符表示他们指向的地址位置之间的关系,指向地址大的指针大于指向地址小的指针;
具有不同数据类型的指针之间的关系运算没有意义,指向不同数据区域的数据两个指针之间,关系运算符也没有意义;
指针与一般整数变量之间的关系运算也没有意义,但是可以和零进行等于或者不等于的关系运算,判断指针是否为空,一般与NULL。
5.2.3 赋值运算
指针变量直接可以直接赋值,但是不能将一个整数赋值给指针变量,因为没有开辟空间;
指针赋值运算是通过赋值运算符向指针变量送一个地址值。
5.2.4 练习:输入一个字符串,将字符串的元素翻转
要求:定义两个指针,分别保存起始位置和末尾位置的字符,然后通过交换的方式实现。
hello world
…
dlrow olleh
答案:
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
char str[32] = {0};
gets(str);
printf("反转之前的字符串:%s\n",str);
char *p = NULL,*q = NULL;
char tmp;
p = &str[0];
q = &str[strlen(str) - 1];
#if 0
while(p < q)
{
tmp = *p;
*p = *q;
*q = tmp;
p++;
q--;
}
#endif
for(;p < q;p++,q--)
{
tmp = *p;
*p = *q;
*q = tmp;
}
printf("反转之后的字符串:%s\n",str);
return 0;
}
六、指针和一维数组
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
int a[5] = {1,2,3,4,5};
int *p = a;
int i;
for(i = 0 ; i < 5;i++)
{
//printf("%d ",a[i]);
printf("%d ",*(p+i));
}
putchar(10);
char *s = "helloworld";
char str[32] ="hello nanjing";
s = str;
//printf("%s\n",s);
for(i = 0 ; i < 10;i++)
{
printf("%c",s[i]);
}
putchar(10);
return 0;
}
练习1:通过指针自己实现strlen函数的功能
#include <stdio.h>
int main(int argc, const char *argv[])
{
char buf[32] = {0};
printf("请输入一个字符串:\n");
gets(buf);
char *p = buf;
int i;
while(*p != '\0')
{
p++;
i++;
}
printf("strlen(buf) = %d\n",i);
return 0;
}
练习2: 通过指针实现strcpy函数的功能
#include <stdio.h>
int main(int argc, const char *argv[])
{
char s1[] = "helloworld!";
char s2[] = "abcdefghijk";
char *p = NULL,*q = NULL;
p = s1;
q = s2;
while(*q != '\0')
{
*p = *q;
p++;
q++;
}
*p = *q;
printf("s1 = %s\n",s1);
return 0;
}
作业:
1.通过指针实现strcat功能
2.实现atoi函数的功能
char str[] = '5891";
int num;
…
“5891” ----->5891
3. 输入一个字符串,输出字符串中有多少个空格
4. 输入两个字符串,判断一个字符串是否为另一个的子串
s1 = hellloworld s2 = oworl