指针-练习
计算字符串长度[1.计数器的方式1; 2.递归的方式2 3.指针的方式3]
int my_strlen(char* str)
{
char* start = str;
char* end = str;
while (*end != '\0')
{
end++;
}
return end - start;//
}
判断打印
int arr[] = { 1, 2, 3, 4, 5 };
short *p = (short*)arr;
int i = 0;
for (i = 0; i<4; i++)
{
*(p + i) = 0;
}
for (i = 0; i<5; i++)
{
printf("%d ", arr[i]);//0 0 3 4 5
}
判断打印
int a = 0x11223344;//小端存储
char *pc = (char*)&a;
*pc = 0;
printf("%x\n", a);
使用指针打印数组内容
#include <stdio.h>
void print(int *p, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
}
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int sz = sizeof(arr) / sizeof(arr[0]);
print(arr, sz);
return 0;
}
注:
- 指针-指针得到指针之间元素的个数,非字节数
写一个函数逆序字符串的内容
#include <stdio.h>
#include <string.h>
#include <assert.h>
void reverse(char *str)
{
assert(str); //判断指针的有效性
int len = strlen(str);
char *left = str;
char *right = str + len - 1;
while (left < right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
int main()
{
char arr[256] = {0};
//scanf("%s", arr);//abcdef ---> fedcba scanf读取遇到空格就会结束
gets(arr); //读取一行
//逆序函数
reverse(arr);
printf("逆序后的字符串:%s\n", arr); //
return 0;
}
计算求和 Sn=a+aa+aaa+aaaa+aaaaa的前5项之和,其中a是一个数字。
int a = 0;
int n = 0;
scanf("%d%d", &a, &n);//2 5
int sum = 0;
int i = 0;
int ret = 0;
//2 22 222 2222 22222
//
for (i = 0; i < n; i++)
{
ret = ret * 10 + a;
sum += ret;
}
printf("%d\n", sum);
判断i是否为水仙花数(自幂数)
#include <stdio.h>
#include <math.h>
int main()
{
int i = 0;
for (i = 0; i <= 100000; i++)
{
//判断i是否为水仙花数(自幂数)
//1. 计算i的位数 - n位数
int n = 1;
int tmp = i;
int sum = 0;
while (tmp /= 10)
{
n++;
}
//2. 计算i的每一位的n次方之和 sum
tmp = i;
while (tmp)
{
sum += pow(tmp % 10, n);
tmp /= 10;
}
//3. 比较i == sum
if (i == sum)
{
printf("%d ", i);
}
}
return 0;
}
打印菱形
*
***
*****
*******
*********
***********
*************
***********
*********
*******
*****
***
*
int line = 0;
scanf("%d", &line);//7
//打印上半部分
int i = 0;
for (i = 0; i < line; i++)
{
//打印空格
int j = 0;
for (j = 0; j < line-1-i; j++)
{
printf(" ");
}
//打印*
for (j = 0; j <2*i+1; j++)
{
printf("*");
}
printf("\n");
}
//打印下半部分
for (i = 0; i < line - 1; i++)
{
int j = 0;
//打印空格
for (j = 0; j <=i; j++)
{
printf(" ");
}
//打印*
for (j = 0; j <2*(line-1-i)-1; j++)
{
printf("*");
}
printf("\n");
}
喝汽水问题
喝汽水,一瓶汽水1元,2个空瓶可以换一瓶汽水,给20元,可以多少汽水?(编程实现)
total=2*m-1
int m = 0;//money
int n = 0;//总数量
int k = 0;//空瓶
scanf("%d", &m);
n = m;
k = m;
while (k >1) {
n += k / 2;
k = k / 2 + k % 2;
}
printf("%d", n);
常量字符串相关
char arr1[] = "abcdef";
char arr2[] = "abcdef";
const char* p1 = "abcdef";//常量字符串
const char* p2 = "abcdef";//常量字符串里
//p1、p2指向的是一个同一个常量字符串,p1和p2指向同一个地址
//C/C++会把常量字符串存储到单独的一个内存区域,
//当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。
//但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。
if (arr1 == arr2)
{
printf("hehe\n");
}
else
{
printf("haha\n");//打印
}
if (p1 == p2)
{
printf("hehe\n");//打印
}
else
{
printf("haha\n");
}
指针和数组相关
数组名是首元素的地址
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
- 除此之外所有的数组名都表示首元素的地址。
一维数组
整形数组
int a[] = {1, 2, 3, 4}; //4*4 = 16
printf("%d\n", sizeof(a)); //sizeof(数组名)-计算的是数组总大小-单位是字节- 16
printf("%d\n", sizeof(a + 0)); //4/8 - 数组名这里表示收元素的值,a+0 还是首元素地址,地址的大小就是4/8个字节
printf("%d\n", sizeof(*a)); //4 - 数组名表示首元素地址,*a就是首元素,sizeof(*a)就是4
printf("%d\n", sizeof(a + 1)); //4/8 - 数组名这里表示收元素的值,a+1 第2个元素的地址,地址的大小就是4/8个字节
printf("%d\n", sizeof(a[1])); //4 - 第2个元素的大小
printf("%d\n", sizeof(&a)); //4/8 &a取出的是数组的地址,但是数组的地址那也是地址,地址的大小就是4/8个字节
printf("%d\n", sizeof(*&a)); //16 - &a数组的地址,数组的地址解引用访问的数组,sizeof计算的就是数组的大小单位是字节
printf("%d\n", sizeof(&a + 1)); //4/8 &a是数组的地址,&a+1虽然地址跳过整个数组,但还是地址,所以是4/8个字节
printf("%d\n", sizeof(&a[0])); //4/8 &a[0]是第一个元素的地址
printf("%d\n", sizeof(&a[0] + 1)); //4/8 &a[0]+1 是第二个元素的地址
字符数组1
char arr[] = {'a', 'b', 'c', 'd', 'e', 'f'};
printf("%d\n", strlen(arr)); //随机值
printf("%d\n", strlen(arr + 0)); //随机值
//printf("%d\n", strlen(*arr));//err
//printf("%d\n", strlen(arr[1]));//err
printf("%d\n", strlen(&arr));//随机值
printf("%d\n", strlen(&arr + 1));//随机值-6,与前一个随机值差6
printf("%d\n", strlen(&arr[0] + 1));//随机值-1,与前一个随机值差1
printf("%d\n", sizeof(arr)); //sizeof计算机的是数组大小,6*1 = 6字节
printf("%d\n", sizeof(arr + 0)); //4/8 arr是首元素的地址,arr+0还是首元素的地址 地址的大小是4/8字节
printf("%d\n", sizeof(*arr)); //1 arr是首元素的地址,*arr就是首元素,首元素是字符大小是一个字节
printf("%d\n", sizeof(arr[1])); //1
printf("%d\n", sizeof(&arr)); //4/8 &arr 虽然是数组的地址,但还是地址,地址大小是4/8个字节
printf("%d\n", sizeof(&arr + 1)); //4/8 &arr+1 是跳过整个数组后的地址,地址大小是4/8个字节
printf("%d\n", sizeof(&arr[0] + 1)); //4/8 第二个元素的地址
字符数组2
char arr[] = "abcdef";
printf("%d\n", strlen(arr)); //6
printf("%d\n", strlen(arr + 0)); //6
//printf("%d\n", strlen(*arr));//err
//printf("%d\n", strlen(arr[1]));//err
printf("%d\n", strlen(&arr)); //6 &arr - 数组的地址-数组指针 char(*p)[7] = &arr;
printf("%d\n", strlen(&arr + 1)); //随机值
printf("%d\n", strlen(&arr[0] + 1)); //5
printf("%d\n", sizeof(arr)); //sizeof(arr)计算的数组的大小,单位是字节:7
printf("%d\n", sizeof(arr + 0)); //4/8 计算的是地址的大小-arr + 0是首元素的地址
printf("%d\n", sizeof(*arr)); //1 *arr 是首元素,sizeof(*arr)计算首元素的大小
printf("%d\n", sizeof(arr[1])); //1 arr[1]是第二个元素,sizeof(arr[1])计算的是第二个元素的大小
printf("%d\n", sizeof(&arr)); //4/8 &arr虽然是数组的地址,但也是地址,所以是4/8个字节
printf("%d\n", sizeof(&arr + 1)); //4/8 &arr+1是跳过整个数组后的地址,但也是地址
printf("%d\n", sizeof(&arr[0] + 1)); //4/8 &arr[0]+1 第二个元素的地址
字符数组3
char *p = "abcdef"; //把常量字符串abcdef的a的地址放到p
printf("%d\n", strlen(p)); //6
printf("%d\n", strlen(p + 1)); //5
//printf("%d\n", strlen(*p));//err
//printf("%d\n", strlen(p[0]));//err
printf("%d\n", strlen(&p)); //随机值 小端存储
printf("%d\n", strlen(&p + 1)); //随机值
printf("%d\n", strlen(&p[0] + 1)); //5
printf("%d\n", sizeof(p)); //4/8 - 计算指针变量p的大小
printf("%d\n", sizeof(p + 1)); //4/8 - p+1 得到的是字符b的地址
printf("%d\n", sizeof(*p)); //1 *p 就是字符串的第一个字符 - 'a'
printf("%d\n", sizeof(p[0])); //1 int arr[10]; arr[0] == *(arr+0) p[0] == *(p+0) == 'a'
printf("%d\n", sizeof(&p)); //4/8 地址 a的地址
printf("%d\n", sizeof(&p + 1)); //4/8 地址 b的地址
printf("%d\n", sizeof(&p[0] + 1)); //4/8 地址 b的地址
&p 和 &p+1 【图中地址为捏造,举例】
二维数组
//二维数组
int a[3][4] = {0};
//printf("%p\n", &a[0][0]); //00000009A9B4F6E8 第一行第一个元素的地址
//printf("%p\n", a[0] + 1); //00000009A9B4F6EC 第一行第二个元素的地址 64位
//printf("%p\n", a + 1); //00000009A9B4F6F8 第二行的地址
//printf("%p\n", &a[0] + 1); //00000009A9B4F6F8 第二行的地址
printf("%d\n", sizeof(a)); //48
printf("%d\n", sizeof(a[0][0])); //4
printf("%d\n", sizeof(a[0])); //16 a[0]相当于第一行做为一维数组的数组名,
//sizeof(arr[0])把数组名单独放在sizeof()内,计算的是第一行的大小
printf("%d\n", sizeof(a[0] + 1)); //4/8 - a[0]是第一行的数组名,数组名此时是首元素的地址,a[0]其实就是第一行第一个元素的地址
//所以 a[0]+1 就是第一行第二个元素的地址- 地址大小是4/8个字节
printf("%d\n", sizeof(*(a[0] + 1))); //4- *(a[0] + 1)) 是第一行第二个元素,大小是4个字节
printf("%d\n", sizeof(a + 1)); //4/8 //a是二维数组的数组名,没有sizeof(a),也没有&(a),所以a是首元素地址
//而把二维数组看成一维数组时,二维数组的首元素是他的第一行,a就是第一行(首元素)的地址
//a+1就是第二行的地址
printf("%d\n", sizeof(*(a + 1))); //16, sizeof(a[1]) 计算第二行的大小,单位是字节
printf("%d\n", sizeof(&a[0] + 1)); //4/8, 第二行的地址
printf("%d\n", sizeof(*(&a[0] + 1))); //16 计算第二行的大小,单位是字节
printf("%d\n", sizeof(*a)); //16 a是首元素地址-第一行的地址,*a就是第一行,sizeof(*a)就是计算第一行的大小
printf("%d\n", sizeof(a[3])); //16 sizeof()括号里不参与真实运算,sizeof(a[3])等价sizeof(a[0])
指针加减整数的运算1
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1)); //2 5
指针加减整数的运算2
//结构体的大小是20个字节
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
} * p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
int main()
{
p = (struct Test *)0x100000;
printf("%p\n", p + 0x1); //00100014 32位 0x100000+20
printf("%p\n", (unsigned long)p + 0x1); //00100001 32位 p转换成10进制=1,048,576+1
printf("%p\n", (unsigned int *)p + 0x1); //00100004 32位 (unsigned int *)无符号整型指针 就是指针+1(int指针为4/8)
return 0;
}
指针加减整数的运算3
int a[4] = { 1, 2, 3, 4 };
int *ptr1 = (int *)(&a + 1);
int *ptr2 = (int *)((int)a + 1);//a是首元素地址,强制转换成整数+1.在转换成地址,相当于向后偏移了一个字节
printf("%x,%x", ptr1[-1], *ptr2);//4,2000000
指针加减整数的运算4
int a[3][2] = { (0, 1), (2, 3), (4, 5) };//(,)逗号表达式,存储逗号后内容
int *p;
p = a[0];
printf("%d\n", p[0]);//1
指针加减整数的运算5
int a[5][5];
int(*p)[4];
p = a;//int (*)[4] ----- int (*) [5] 【会有警告】
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);//FFFFFFFC,-4
指针加减整数的运算6
int aa[2][5] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *ptr1 = (int *)(&aa + 1);
int *ptr2 = (int *)(*(aa + 1)); // aa[1]
printf("%d,%d\n", *(ptr1 - 1), *(ptr2 - 1)); //10 5
int arr[10] = {1,2,3,4,5};
int* p = arr;
//*(p + 2) == p[2] ==> *(arr+2) == arr[2]
arr[2]; // ==> *(arr+2)
指针加减整数的运算7
char* a[] = { "work", "at", "alibaba" };
char**pa = a;
pa++;
printf("%s\n", *pa);//
指针加减整数的运算8
char *c[] = {"ENTER", "NEW", "POINT", "FIRST"};
char **cp[] = {c + 3, c + 2, c + 1, c};
char ***cpp = cp;
printf("%s\n", **++cpp); //POINT
printf("%s\n", *--*++cpp + 3); //ER
printf("%s\n", *cpp[-2] + 3); //ST
printf("%s\n", cpp[-1][-1] + 1); //EW
旋转字符串
实现一个函数,可以左旋字符串中的k个字符。
例如:
ABCD左旋一个字符得到BCDA
ABCD左旋两个字符得到CDAB
-
暴力求解法
#include <string.h> #include <assert.h> void left_move(char* arr, int k) { assert(arr != NULL); int i = 0; int len = strlen(arr); for (i = 0; i < k; i++) { //左旋转一个字符 //1 char tmp = *arr; //2 int j = 0; for (j = 0; j < len-1; j++) { *(arr + j) = *(arr + j + 1); } //3 *(arr + len - 1) = tmp; } } int main() { char arr[] = "abcdef"; left_move(arr, 2); printf("%s\n", arr); return 0; }
-
三步翻转法
abcdef ab cdef 左边逆序,右边逆序 bafedc 整体逆序 cdefab
#include <string.h> #include <assert.h> //逆序字符串的函数 void reverse(char* left, char* right) { assert(left != NULL); assert(right != NULL); while (left < right) { char tmp = *left; *left = *right; *right = tmp; left++; right--; } } void left_move(char* arr, int k) { assert(arr); int len = strlen(arr); assert(k <= len); //不能超过字符长度 reverse(arr, arr + k - 1);//逆序左边 reverse(arr + k, arr + len - 1);//逆序右边 reverse(arr, arr + len - 1);//逆序整体 } int main() { char arr[] = "abcdef"; left_move(arr, 2); printf("%s\n", arr); return 0; }
字符串旋转结果
写一个函数,判断一个字符串是否为另一个字符串旋转之后的字符串。
例如给定 s1=AABCD和s2=BCDAA,返回1
给定s1=abcd和s2=ACBD,返回0
AABCD左旋一个字符得到ABCDA
AABCD左旋两个字符得到BCDAA
AABCD右旋一个字符得到DAABC
-
穷举法
int is_left_move(char* str1, char* str2) { int len = strlen(str1); int i = 0; for (i = 0; i < len; i++) { left_move(str1, 1);//调用上题代码,每次旋转s1都会变化 int ret = strcmp(str1, str2); if (ret == 0) { return 1; } } return 0; } int main() { char arr1[] = "abcdef"; char arr2[] = "cdefab"; int ret = is_left_move(arr1, arr2); if (ret == 1) { printf("Yes\n"); } else { printf("No\n"); } return 0; }
-
追加法
abcdef cdefab abcdefabcdef
#include <string.h> #include <assert.h> int is_left_move(char* str1, char* str2) { int len1 = strlen(str1); int len2 = strlen(str2); if (len1 != len2) return 0; //1. 在str1字符串中追加一个str1字符串 //strcat(str1, str1);//err 自己给自己追加不能使用该方法\0不会停下来 strncat(str1, str1, len1);//abcdefabcdef //2. 判断str2指向的字符串是否是str1指向的字符串的子串 //strstr-找子串的 char * ret = strstr(str1, str2); if (ret == NULL) { return 0; } else { return 1; } } int main() { char arr1[30] = "abcdef"; char arr2[] = "cdef"; int ret = is_left_move(arr1, arr2); if (ret == 1) { printf("Yes\n"); } else { printf("No\n"); } return 0; }
注: strcat 示例
#include <string.h> #include <stdio.h> int main() { char arr1[30] = "abc"; char arr2[] = "def"; strcat(arr1, arr2); printf("%s\n", arr1); //abcdef return 0; }
杨氏矩阵
有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,
请编写程序在这样的矩阵中查找某个数字是否存在。
要求:时间复杂度小于O(N);
1 2 3
4 5 6
7 8 9
1 2 3
2 3 4
3 4 5
//int FindNum(int arr[3][3], int k, int row, int col)
//{
// int x = 0;
// int y = col - 1;
//
// while (x<=row-1 && y>=0)
// {
// if (arr[x][y] > k)
// {
// y--;
// }
// else if (arr[x][y] < k)
// {
// x++;
// }
// else
// {
// return 1;
// }
// }
// //找不到
// return 0;
//}
//
int FindNum(int arr[3][3], int k, int *px, int*py)
{
int x = 0;
int y = *py - 1;
while (x <= *px - 1 && y >= 0)
{
if (arr[x][y] > k)
{
y--;
}
else if (arr[x][y] < k)
{
x++;
}
else
{
*px = x;
*py = y;
return 1;
}
}
//找不到
return 0;
}
int main()
{
int arr[3][3] = { { 1, 2, 3 }, { 4, 5, 6 }, {7,8,9} };
int k = 7;
int x = 3;
int y = 3;
//返回型参数
int ret = FindNum(arr, k, &x, &y);
if (ret == 1)
{
printf("找到了\n");
printf("下标是:%d %d\n", x, y);
}
else
{
printf("找不到\n");
}
return 0;
}