I clearly am doing something wrong but, for the life of me, can't figure out what.
int main(int argc, char *argv[])
{
int done=0;
int end=0;
int didswap=0;
char *temp[2] = {0};
int i;
int x;
printf("This function Bubble sorts the Flintstones in alphabetical order!\n");
printf("The Flintstones names are:\nFred\nBarney\nWilma\nPebbles\nDino\n");
char *names[5] = {0};
names [0] = "Fred";
names [1] = "Barney";
names [2] = "Wilma";
names [3] = "Pebbles";
names [4] = "Dino";
while(end == 0)
{
for(i=0;i<4;i++)
{
if (strcmp(names[i],names[i+1])>0)
{
strcpy(temp[0],names[i]);
strcpy(temp[1],names[i+1]);
strcpy(names[i],temp[1]);
strcpy(names[i+1],temp[0]);
didswap = 1;
}
else
{
didswap = 0;
}
done = done+didswap;
}
if (done == 0)
end = 1;
else
done = 0;
}
printf("When alphabetized they are:\n");
for (i = 0; i < 5; i++)
{
printf("%s \n", names[i]);
}
system("PAUSE");
return EXIT_SUCCESS;
}
You have an array of string literals. These may be held in read only memory so you can't change their content. You can however change the order you store pointers to them in names by replacing
strcpy(temp[0],names[i]);
strcpy(temp[1],names[i+1]);
strcpy(names[i],temp[1]);
strcpy(names[i+1],temp[0]);
with
const char* tmp = names[i];
names[i] = names[i+1];
names[i+1] = tmp;
strcpy(temp[0],names[i]);
strcpy(temp[1],names[i+1]);
strcpy(names[i],temp[1]);
strcpy(names[i+1],temp[0])
names strings are string literals and string literals are immutable in C. Attempting to modifiy a string literal invokes undefined behavior.
Related
I am attempting to write the substitution program here https://cs50.harvard.edu/x/2022/psets/2/substitution/
here is my code:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
int main(int argc, string argv[1])
{
if (argc > 2 || argc < 2){
printf("Plz 1 word in command \n");
return 1;
}
int sum = 0;
string arg1 = argv[1];
//test
for (int i = 0; i < strlen(arg1); i++){
if (isalpha(arg1[i]) == 0){
printf("plz only alphabet character \n");
return 1;
}}
// convert all key to upper
for (int i = 0; i < strlen(arg1); i++){
arg1[i] = toupper(arg1[i]);
}
for (int i = 0; i < strlen(arg1); i++){
sum = sum + (int)(arg1[i]);
}
if (strlen(arg1) != 26){
printf("Plz input 26 char \n");
return 1;
} else if (sum != 2015){printf("no oveerlapping letter plz \n");
return 1; }
//test finish
string al = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string pt = get_string("plaintext: ");
char pt1[strlen(pt)];
char cp[strlen(pt)];
// all plain text to upper
for (int i = 0; i < strlen(pt); i++){
pt1[i]=toupper(pt[i]);
}
//scan
for (int a = 0; a < strlen(pt); a++){
char b = pt1[a];
for (int i = 0; i < strlen(al); i++){
if ( al[i] == b){
cp[a] = arg1[i];
break;
} else {
cp[a] = b;
}
}
//case preserve
if (islower(pt[a])){
cp[a] = tolower(cp[a]);
}}
printf("ciphertext: %s \n", cp);
return 0;
}
when i type in the key YTNSHKVEFXRBAUQZCLWDMIPGJO and then type "hello!1 lmao" as plaintext, here is what i receive
substitution/ $ ./substitution YTNSHKVEFXRBAUQZCLWDMIPGJO
plaintext: hello!1 lmao
ciphertext: ehbbq!1 bayq�
it should only show ehbbq!1 bayq but it is showing more letter than i intended,
there might be other letter or simbol after "bayq", can someone explain to me what is going on and why there are additional text in my output?
You need a null terminatig character (usually it is character having integer value 0) to terminate the string
size_t a;
for (a = 0; a < strlen(pt); a++)
{
char b = pt1[a];
for (int i = 0; i < strlen(al); i++)
{
if ( al[i] == b)
{
cp[a] = arg1[i];
break;
} else {
cp[a] = b;
}
}
//case preserve
if (islower((unsigned char)pt[a]))
{
cp[a] = tolower((unsigned char)cp[a]);
}
}
cp[a] = 0;
you need to pass unsigned char to functions like tolower. I did not analyze the logic of your code as it is your home work.
Also cp is too short, it has to be char cp[strlen(pt) + 1];
Your char arrays as declared are too short to handle copies of the data, due to the need for a null-terminator att the end.
char pt1[strlen(pt)];
char cp[strlen(pt)];
Rather you need to do:
char pt1[strlen(pt) + 1];
char cp[strlen(pt) + 1];
However, the other approach would be to simply use strdup to dynamically allocate sufficient storage and copy the data.
char pt1 = strdup(pt);
char cp = strdup(pt);
Of course, any function that returns dynamically allocated memory (likely including cs50's get_string) means you should remember to free that memory. And ensure it actually succeeded.
I'm trying to instantiate a new string every iteration of a loop. What I tried was this:
int main(void) {
char *strs[10];
for (int i = 0; i < 10; i++) {
char str[10] = "";
char c = '0' + i;
strncat(str, &c, 1);
strs[i] = str;
}
for (int i = 0; i < 10; i++) {
printf("%s\n", strs[i]);
}
}
However, this prints all 9's, since C has conveniently decided that each iteration it would use the same address. I know that the following works, but just wanted to ask if it is possible to avoid malloc.
int main(void) {
char *strs[10];
for (int i = 0; i < 10; i++) {
char *str = malloc(sizeof(char) * 10);
char c = '0' + i;
strncat(str, &c, 1);
strs[i] = str;
}
for (int i = 0; i < 10; i++) {
printf("%s\n", strs[i]);
}
}
I am also interested in instantiating structs the same way without malloc. Is this possible?
A straightforward approach is to declare a two dimensional array like for example
char strs[10][2] = { "" };
for (int i = 0; i < 10; i++) {
strs[i][0] = '0' + i;
}
Here is a demonstration program
#include <stdio.h>
int main( void )
{
enum { N = 10 };
char strs[N][2] = { "" };
for ( int i = 0; i < N; i++ )
{
strs[i][0] = '0' + i;
}
for ( int i = 0; i < N; i++ )
{
printf( "%s ", strs[i] );
}
putchar( '\n' );
}
The program output is
0 1 2 3 4 5 6 7 8 9
Pay attention to that your second program has undefined behavior
char *str = malloc(sizeof(char) * 10);
char c = '0' + i;
strncat(str, &c, 1);
strs[i] = str;
The dynamically allocated array is not initialized. So the call of strncat invokes undefined behavior.
And in the both your programs this call strncat(str, &c, 1); does not build a string. So this call printf("%s\n", strs[i]);again invokes undefined behavior.
Regardless of data type, an object declared at block scope without storage class specifier static* has automatic storage duration. Its lifetime ends when execution of the innermost block containing its declaration terminates. Attempting to access such an object outside its lifetime produces undefined behavior.
If you want an object whose lifetime starts after the beginning of program execution and extends beyond the termination of the innermost block containing the start point, then dynamic allocation is your only choice.
* with static, the object has static storage duration -- the whole run of the program. You can access these via pointer from outside the scope of their identifiers, but for each such declaration there is only object serving the whole execution of the program.
"just wanted to ask if it is possible to avoid malloc."
Yes. The following makes use of Variable Length Arrays, and a function to do this without using calloc or malloc.
Running from the command prompt for example, enter:
your_program_name 10 5<return> to create an array of 10 elements each having room for 4 char plus a terminating null.
void populate_strs(int num, int longest, char sArr[num][longest])
{
for(int i=0;i<num; i++)
{
char c[2] = {0};
c[0] = '0' + (char)i;
c[1] = 0;//null terminate string
strncat(sArr[i], c, 1);//do not need & for string
}
}
int main(int argc, char *argv[])
{
if(argc != 3) printf("Usage error...");
int num_str = atoi(argv[1]);
int max_len = atoi(argv[2]);
char strs[num_str][max_len];
memset(strs, 0, sizeof strs);
populate_strs(num_str, max_len, strs);
for (int i = 0; i < 10; i++) {
printf("%s\n", strs[i]);
}
return 0;
}
And the same is true for an array of struct;
Given this for example:
typdef struct {
int val;
char str;
bool state;
} data_s;
...
int size = 0;
printf("Enter how many array elements of data_s to create.\n");
fscanf(stdin, "%d", &size);
data_s data[size];
memset(data, 0, sizeof data);
//use array of data;
I am trying to compare strings received from the command line arguments without using the string library. I have created a function to compare char*'s and it works for strings I create in my program, but not on strings passed in the command line. Here is the code for reference.
#include <stdio.h>
int comparestr(char* a, char* b) {
printf("string 1 = %s, string 2 = %s\n", a, b);
int len_a =0, len_b=0;
while(*a) {
len_a++;
a++;
}
while(*b) {
len_b++;
b++;
}
printf("len a = %d, len b = %d\n", len_a, len_b);
if (len_a == len_b) {
for(int i=0; i<len_a; i++) {
if(*(a+i) != *(b+i)) {
return 0;
}
}
return 1;
}
else
return 0;
}
int main(int argc, char**argv) {
char* str1 = "hello";
char* str2 = "hello";
if(comparestr(str1,str2))
printf("Same string.\n");
else
printf("Different strings.\n");
if(comparestr(*(argv+1),"hell"))
printf("Same cl string.\n");
else
printf("Different cl strings.\n");
return 0;
}
Here is an example of the output.
./a.out hell hell
string 1 = hello, string 2 = hello
len a = 5, len b = 5
Same string.
string 1 = hell, string 2 = hell
len a = 4, len b = 4
Different cl strings.
The issue with your code is that you change the pointer by incrementing it when you try to get the length of the strings. Once you change the pointers, the pointers will not point to the base of your strings.
Instead of changing your pointer, make a copy of it, and use the copy instead to calculate the length of the string, that way you don't lose the base address of your strings.
New code with changes.
//Copy of the pointer, to change this instead and keep the original pointer address
char* ptr_a = a;
char* ptr_b = b;
int len_a =0, len_b=0;
while(*ptr_a) {
len_a++;
ptr_a++;
}
while(*ptr_b) {
len_b++;
ptr_b++;
}
The whole code.
#include <stdio.h>
int comparestr(char* a, char* b) {
printf("string 1 = %s, string 2 = %s\n", a, b);
//Copy of the pointer, to change this instead and keep the original pointer address
char* ptr_a = a;
char* ptr_b = b;
int len_a =0, len_b=0;
while(*ptr_a) {
len_a++;
ptr_a++;
}
while(*ptr_b) {
len_b++;
ptr_b++;
}
printf("len a = %d, len b = %d\n", len_a, len_b);
if (len_a == len_b) {
for(int i=0; i<len_a; i++) {
if(*(a+i) != *(b+i)) {
return 0;
}
}
return 1;
}
else
return 0;
}
int main(int argc, char**argv) {
char* str1 = "hello";
char* str2 = "hello";
if(comparestr(str1,str2))
printf("Same string.\n");
else
printf("Different strings.\n");
if(comparestr(*(argv+1),"hell"))
printf("Same cl string.\n");
else
printf("Different cl strings.\n");
return 0;
}
New Output
Command-line arguments: hell hell
string 1 = hello, string 2 = hello
len a = 5, len b = 5
Same string.
string 1 = hell, string 2 = hell
len a = 4, len b = 4
Same cl string.
Assignment: Write a function that assumes the UPC code is an audio/video product, and returns a string indicating which product it is. this is the last function i have to write for this lab and I'm having trouble figuring out how to return a character array from this function. Barcode array is a 12 digit upc code entered by the user and the 10 space in the array indicated which type of media it is. After checking what the digit is, I want to return the indicated character array back to main to be printed.
char *getAudioVideoProductString(int barcodeArray[12]);
int main()
{
int barcodeArray[12];
do{
printf("\nEnter UPC code: ");
for(i = 0; i < 12; i++)
{
scanf("%d", &barcodeArray[i]);
}
avstring = getAudioVideoProductString(barcodeArray);
for(a = 0; a != '\0'; a++)
{
printf("%c", avstring[a]);
}
printf("\nAdd another UPC code [1 = add, 0 = quit]\n");
scanf("%d",&end);
}while(end !=0);
return EXIT_SUCCESS;
}
char *getAudioVideoProductString(int barcodeArray[12])
{
int i;
if (barcodeArray[10] == 1)
{
char b[] = "12\"\ LP or Single";
int lengthb = strlen(b);
char a[] = malloc(sizeof(char)*100);
for (i = 0; i < lengthb; i++)
{
a[i] = b[i];
}
}
if (barcodeArray[10] == 0| barcodeArray[10] == 3| barcodeArray[10] == 6)
{
char b[] = "Unassigned Audio Format";
int lengthb = strlen(b);
char a[] = malloc(sizeof(char)*100);
for (i = 0; i < lengthb; i++)
{
a[i] = b[i];
}
}
return a;
}
When I compile the program I get these errors:
char a[] = malloc(sizeof(char)*100); //-<< wrong a is not the pointer only the array
char *a = malloc(sizeof(char)*100); // -<< correct. you need the pointer to assign the address of the allocated memory
I think you need to read a bit about the arrays and pointers. Here on the SO are many topics about them. Just search.
PS
if you need the array:
char a[100];
wordCur is a string of capital letters, and dictionary is an array of strings, no matter what I input into wordCur, I am always returned 0.
Edit: I updated the code a little bit, and added an abridged version of the rest of the program for some context. As it is shown here, it just crashes when it gets to checkValid
int main() {
FILE *ifp;
ifp = fopen("dictionary.txt", "r");
int* lDist[26];
int* lUsed[26];
int dictLen;
int i;
fscanf(ifp, "%d", &dictLen);
char dictionary[dictLen][7];
char* letters[7];
int scoreCur = 0;
int scoreHi = 0;
char wordCur[7];
char wordHi[7];
int isWord = 0;
//reads the dictionary into the array
for (i = 0; i < dictLen; i++) {
fscanf(ifp, "%s", &dictionary[i]);
}
scanf("%s", wordCur);
isWord = checkValid(wordCur, dictLen, dictionary);
if (isWord == 1) {
scoreCur = calcScore(wordCur);
}
//fclose(ifp); not sure why, but this causes a crash
return 0;
}
int checkValid (char *wordCur,int dictLen, char dictionary[dictLen]) {
int valid = 0;
int i;
for (i = 0; i < dictLen; i++){
int helper = strcmp(wordCur, dictionary[i]);
if (helper = 0){
valid = 1;
}
}
wordCur is a string of capital letters
int checkValid (char wordCur,int dictLen, char dictionary[dictLen])
No, wordCur is a single character. Not a string. A string in C is represented as an array of characters, terminated by a character with the value 0. You need a pointer argument, char *wordCur.
Your code should probably look more like this:
int checkValid(const char *wordCur, // word to search for (string)
int dictLen, // no of entries in dictionary
char dictionary[][7]) // dictionary (array of strings)
{
int valid = 0;
int i;
for (i = 0; i < dictLen; i++)
{
if (strcmp(wordCur, dictionary[i]) == 0)
{
valid = 1;
break;
}
}
return valid;
}
wordCur is a string of capital letters, and dictionary is an array of strings
Try this:
int checkValid (const char *wordCur,int dictLen, const char *dictionary[])
By the way, you keep searching, even after you found what you are looking for, and the comaprison is wrong anyway for strings. I suggest:
for (i = 0; i < dictLen; i++){
if (strcmp(wordCur, dictionary[i]) == 0){
valid = 1;
break;
}
}