Pointers to Structures in C - c

When trying to compile the following code, I am getting a warning that line 18 makes integer from pointer without cast and that 19 and 20 are incompatible types in assignment. I am new to structures in C, and can't seem to figure out what is wrong.
#include <stdio.h>
struct song
{ char title[70];
};
struct playlist
{ struct song songs[100];
};
void title_sort(struct playlist * list,int len)
{ int swapped = 1,i;
char hold;
while (swapped)
{ swapped = 0;
for (i = 0;i < len - 1; i++)
{ if (list->songs[i].title > list->songs[i+1].title)
{ hold = list->songs[i].title;
list->songs[i].title = list->songs[i+1].title;
list->songs[i+1].title = hold;
swapped = 1;
}
}
}
}
int main()
{ struct playlist playlist;
int i;
for (i = 0;i < 5;i++)
{ fgets(playlist.songs[i].title,70,stdin);
}
title_sort(&playlist,5);
printf("\n");
for (i = 0;i < 5;i++)
{ printf("%s",playlist.songs[i].title);
}
return 0;
}

You can't compare strings in C with >. You need to use strcmp. Also hold is char but title is char [70]. You could copy pointers to strings but arrays can't be copied with just =.
You could use strcpy like this:
void title_sort(struct playlist * list,int len)
{ int swapped = 1,i;
char hold[70];
while (swapped)
{ swapped = 0;
for (i = 0;i < len - 1; i++)
{ if (strcmp (list->songs[i].title, list->songs[i+1].title) > 0)
{ strcpy (hold, list->songs[i].title);
strcpy (list->songs[i].title, list->songs[i+1].title);
strcpy (list->songs[i+1].title,hold);
swapped = 1;
}
}
}
}
But please note that in C you need to check things like the lengths of strings, so the above code is dangerous. You need to either use strncpy or use strlen to check the lengths of the strings.

You can not use strings like that C. Strings are essentially a simple array of characters in C without specialized operators like =, < etc. You need to use string functions like strcmp and strcpy to do the string manipulations.

To be more specific : following is wrong
if (list->songs[i].title > list->songs[i+1].title)
Do it this way:
if( strcmp (list->songs[i].title , list->songs[i+1].title) > 0 )

char hold needs to be something else, perhaps char *hold, perhaps an array.
C doesn't have array assignment, although it does have structure assignment, you will need to rework that

Your first issue, on line 18, is caused by two problems. Firstly, the variable hold can only hold a single char value, and you're trying to assign an array of 70 chars to it.
First you'll need to make hold the correct type:
char hold[70];
Now, there's another problem - arrays can't just be assigned using the = operator. You have to use a function to explicitly copy the data from one array to another. Instead of your current line 18, you could use:
memcpy(hold, list->songs[i].title, 70);
You then need to do the same thing for lines 19 and 20:
memcpy(list->songs[i].title, list->songs[i+1].title, 70);
memcpy(list->songs[i+1].title, hold, 70);
Alternatively, you could write a loop and swap the two titles one char at a time.
In a similar fashion, you can't compare two strings with the simple < operator - you need to use a function for this, too (eg. strcmp).

Related

qsort() function in c used to compare an array of strings

I am trying to recreate the Linux command ls in C. I have the program working (going through a list of directories passed in as command line arguments, and pushing all the contents to an array of strings).
Now I'm trying to implement the quicksort flag for the command (e.g. ls -s /dev), which should print out all the contents in a lexicographical order. The problem is that the qsort() method in stdlib.h only "sorts" one element (basically swaps the first and the last element in the array) for my program.
I have no idea what's wrong, as all my pointers are properly set up as well. I'm adding the relevant snippet codes below, please let me know if something catches your eye that has been escaping mine for two days.
Compare function for qsort:
int normalCompare (const void *stringOne, const void *stringTwo) {
return strcmp((const char *)stringOne, (const char *)stringTwo);
}
Actual function call:
void execute_ls(char **directoryList, Flags flags) {
//Create a buffer for directories' file names
char **fileNamesList;
fileNamesList = malloc(MAX_FILES * sizeof (*fileNamesList));
int fileBufferCurrentPointer = 0;
//Fill the buffer out by calling execute_ls_one_dir on all the directories
int i = 0;
while(directoryList[i] != NULL) {
execute_ls_one_dir(directoryList[i], fileNamesList, &fileBufferCurrentPointer);
i++;
}
fileNamesList[fileBufferCurrentPointer] = NULL;
//Process the array
if(flags.s == 1) {
qsort(fileNamesList, fileBufferCurrentPointer, sizeof (char *), normalCompare);
}
else if(flags.r == 1) {
qsort(fileNamesList, fileBufferCurrentPointer, sizeof (char *), reverseCompare);
}
//Print to user
for(i = 0; i < fileBufferCurrentPointer; i++) {
if(((*fileNamesList[i] == '.') && (flags.a == 1)) || (*fileNamesList[i] != '.')) {
printf("%s\n", fileNamesList[i]);
}
}
//Deallocate fileNamesList
for(i = 0; i < MAX_FILES; i++) {
free(fileNamesList[i]);
}
free(fileNamesList);
}
Updating the fileBufferCurrentPointer:
while((oneDirEntryPtr = readdir(currentDirPtr)) != NULL) {
// Push the file name onto the fileNamesList array
fileNamesList[*fileBufferCurrentPointer] = malloc(MAX_LEN_NAME * sizeof (char));
strcpy(fileNamesList[*fileBufferCurrentPointer], oneDirEntryPtr->d_name);
*fileBufferCurrentPointer += 1;
}
I'm confused as to why qsort is only working once (technically isn't even passing through the array once instead of recursively multiple times to complete the algorithm).
you've made a common mistake in thinking the comparison function is taking two elements from your array - it's actually taking two pointers to elements in your array so you should call strcmp like this
int normalCompare (const void *stringOne, const void *stringTwo) {
return strcmp(*(const char **)stringOne, *(const char **)stringTwo);
}

Clear char array in C without any standard library

I'm working on a class project that would require me to make unique strings and I want to concatenate a number to a string. However I do NOT have access to C Standard Library (memset, malloc, etc.). I made this which works:
char* concat(char* name, int num) {
int i, j;
char newName[50], stack[5];
for(i=0; name[i]!='\0'; ++i) {
newName[i] = name[i];
}
for (j=0; num>=1 || num==0; j++) {
stack[j] = (num % 10) + '0';
num = num / 10;
if (num==0) break;
}
while (j>=0) {
newName[i++] = stack[j--];
}
name[0] = '\0';
return newName;
}
But then as I tested it with multiple strings, I realized that newName was being reused over and over. For ex.
This test file outputs the following:
int main() {
char* rebecca = concat("rebecca", 1);
char* bill = concat("bill", 2);
Write(rebecca); /* bill2ca1 */
Write(bill); /* bill2ca1 */
}
It successfully appends the 1 to rebecca, but then when I call concat on bill, it overwrites the first 5 letter but keeps the same chars from before in newName.
QUESTION: How to clear a char array so the next time it's called it will be set to empty, or dynamically allocate it (without using C Standard Library)?
Without using malloc, you can simply put the memory on the stack of the calling function, to keep in the scope where it is needed. It's easier to add the buffer pointer to the argument list like so:
char* concat(char *newName, char* name, int num) {
int i, j;
char stack[5];
:
:
}
int main() {
char rebecca[50];
char bill[50];
concat(rebecca, "rebecca", 1);
concat(bill, "bill", 2);
write(rebecca);
write(bill);
}
Generally speaking, assign memory where it will be used. Embedded programming (which might need to run for months without a reboot) avoids malloc like the plague, just because of the risk of memory leaks. You then need to assign extra space since you may not know the size at compile time, and then ideally check for running past the end of the buffer. Here we know the string sizes and 50 chars is more than enough.
Edit:
The other issue is that you're not null terminating. The print will go until it hits 0x00. Your line
name[0] = '\0';
should be
newName[i] = '\0';
You've got a major issue that you're overlooking. In your function, newName is a local variable (array) and you're returning it from the function. This invokes undefined behavior. The beauty of UB is that, sometime it appears to work as expected.
You need to take a pointer and allocate memory dynamically instead, if you want to return it from your concat() function. Also, in the main(), after using it, you need to free() it.
A better alternative, maybe, if you choose to do so, is
Define the array in the caller.
Pass the array to the function.
Inside the function, memset() the array before you perform any other operation.
One thing to remember, this way, every call to the function will clean the previous result.
EDIT:
If you cannot use memset(), in the main, you can use a for loop like
for (i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
arr[i] = 0;
to clear the array before passing it on next time.
You're returning the address of a local variable. Since the variable goes out of scope when the function returns, this invokes undefined behavior.
You function should dynamically allocate memory for the result of the concatenation, then return that buffer. You'll need to be sure to free that buffer later to prevent a memory leak:
char* concat(char* name, int num) {
int i, j;
char *newName, stack[5];
// allocate enough space for the existing string and digits for a 64-bit number
newName = malloc(strlen(name) + 30);
for(i=0; name[i]!='\0'; ++i) {
newName[i] = name[i];
}
for (j=0; num>=1 || num==0; j++) {
stack[j] = (num % 10) + '0';
num = num / 10;
if (num==0) break;
}
while (j>=0) {
newName[i++] = stack[j--];
}
newName[i] = '\0';
return newName;
}
int main() {
char* rebecca = concat("rebecca", 1);
char* bill = concat("bill", 2);
Write(rebecca);
Write(bill);
free(rebecca);
free(bill);
}

C, write in array in a empty space

I have a problem. i'm writting a C code, but my problem is this.
int func(const char* path, httpCallback_t handlerFunc) {
int i;
int max = 4;
for (i = 0; i < max; ++i) {
UriArray[i];
func_ptr[i];
}
return 0;
}
I would add *path and handlerFunc to UriArray[] and func_ptr[] But the constraint is that each time these values ​​are loaded in an empty spac
Regards
UriArray[i];
is an expression which "returns" the current value of UriArray[i] then throws it away. It's about as useful (without side effects) as the statement:
42;
If you want to store something in there, you need to assign to it, with something like:
UriArray[i] = 314159;
Assuming you don't have backing storage for the strings, it's likely to be something like:
UriArray[i] = strdup (path);
func_ptr[i] = handlerFunc;
But keep in mind that will set all four entries in the arrays to be the same value. That's probably not what you want. It's more likely that you want to append a single entry to the array in which case you'll need to keep a copy of the current size, something like:
int size = 0;
#define MAX 4
char *UriArray[MAX];
httpCallback_t func_ptr[MAX];
int func(const char* path, httpCallback_t handlerFunc) {
if size == MAX) return -1;
UriArray[size] = strdup (path);
func_ptr[size++] = handlerFunc;
return 0;
}

Concatenation in C with 2D char array

I am reading in a textfile line by line into a 2D array. I want to concatenate the char arrays so I have one long char array. I am having trouble with this, I can get it to work with two char arrays but when I try to do a lot of them I go wrong.
Currently the char arrays look like this:
AGCTTTTCATTC
I want to get something like this:
AGCTTTTCATTCAGCTTTTCATTC
I have inlcuded some of my code.
int counter = 0;
fid = fopen("dna.fna","r");
while(fgets(line, sizeof(line), fid) != NULL && counter!=66283 ) {
if (strlen(line)==70) {
strcpy(dna[counter], line);
counter++;
}
}
int dnaSize = 6628;
//Concatenating the DNA into a single char array.
int i;
char DNA[dnaSize];
for(i = 0; i<66283;i++){
strcpy(DNA[i],dna[i]);
strcat(DNA[i+1],dna[i+1]);
}
You need to loop only up to < counter
Then, are you copying or concatenating? You only need to do one or the other.
I suggest just use strcat in the loop, but initialise DNA.
char DNA[dnaSize] = ""; //initalise so safe to pass to strcat
for(i = 0; i<counter;i++)
{
strcat(DNA,dna[i]); //no need for indexer to DNA
}
Also, you need to consider the sizes of your two arrays. I believe (hope) that dna is an array of an array of char. If it is, I guess it is 66283 long in it's first dimension alone. So it is not going to fit into DNA (6628 long), even if each line was 1 char in length.
Here is an idea on how to allocate exactly the right amount of memory:
#define MAXLINELENGTH (70)
#define MAXDNALINES (66283)
//don't copy this line, it will not work because of the sizes involved (over 4MB)
//it will likely stack overflow
//just do what you are currently doing as long as it's a 2-d array.
char dna[MAXDNALINES][MAXLINELENGTH + 1];
int counter = 0;
int totalSize = 0;
fid = fopen("dna.fna","r");
while(fgets(line, sizeof(line), fid) != NULL && counter!=MAXDNALINES ) {
const int lineLength = strlen(line);
if (lineLength==MAXLINELENGTH) {
strcpy(dna[counter], line);
counter++;
totalSize += lineLength;
}
}
//Concatenating the DNA into a single char array (of exactly the right length)
int i;
char *DNA = malloc(totalSize+1); // the + 1 is for the final null, and putting on heap so don't SO
DNA[0] = '\0'; //the initial null is so that the first strcat works
for(i = 0; i<counter;i++){
strcat(DNA,dna[i]);
}
//do work with DNA here
//finally free it
free(DNA);

Appending a char to a char* in C?

I'm trying to make a quick function that gets a word/argument in a string by its number:
char* arg(char* S, int Num) {
char* Return = "";
int Spaces = 0;
int i = 0;
for (i; i<strlen(S); i++) {
if (S[i] == ' ') {
Spaces++;
}
else if (Spaces == Num) {
//Want to append S[i] to Return here.
}
else if (Spaces > Num) {
return Return;
}
}
printf("%s-\n", Return);
return Return;
}
I can't find a way to put the characters into Return. I have found lots of posts that suggest strcat() or tricks with pointers, but every one segfaults. I've also seen people saying that malloc() should be used, but I'm not sure of how I'd used it in a loop like this.
I will not claim to understand what it is that you're trying to do, but your code has two problems:
You're assigning a read-only string to Return; that string will be in your
binary's data section, which is read-only, and if you try to modify it you will get a segfault.
Your for loop is O(n^2), because strlen() is O(n)
There are several different ways of solving the "how to return a string" problem. You can, for example:
Use malloc() / calloc() to allocate a new string, as has been suggested
Use asprintf(), which is similar but gives you formatting if you need
Pass an output string (and its maximum size) as a parameter to the function
The first two require the calling function to free() the returned value. The third allows the caller to decide how to allocate the string (stack or heap), but requires some sort of contract about the minumum size needed for the output string.
In your code, when the function returns, then Return will be gone as well, so this behavior is undefined. It might work, but you should never rely on it.
Typically in C, you'd want to pass the "return" string as an argument instead, so that you don't have to free it all the time. Both require a local variable on the caller's side, but malloc'ing it will require an additional call to free the allocated memory and is also more expensive than simply passing a pointer to a local variable.
As for appending to the string, just use array notation (keep track of the current char/index) and don't forget to add a null character at the end.
Example:
int arg(char* ptr, char* S, int Num) {
int i, Spaces = 0, cur = 0;
for (i=0; i<strlen(S); i++) {
if (S[i] == ' ') {
Spaces++;
}
else if (Spaces == Num) {
ptr[cur++] = S[i]; // append char
}
else if (Spaces > Num) {
ptr[cur] = '\0'; // insert null char
return 0; // returns 0 on success
}
}
ptr[cur] = '\0'; // insert null char
return (cur > 0 ? 0 : -1); // returns 0 on success, -1 on error
}
Then invoke it like so:
char myArg[50];
if (arg(myArg, "this is an example", 3) == 0) {
printf("arg is %s\n", myArg);
} else {
// arg not found
}
Just make sure you don't overflow ptr (e.g.: by passing its size and adding a check in the function).
There are numbers of ways you could improve your code, but let's just start by making it meet the standard. ;-)
P.S.: Don't malloc unless you need to. And in that case you don't.
char * Return; //by the way horrible name for a variable.
Return = malloc(<some size>);
......
......
*(Return + index) = *(S+i);
You can't assign anything to a string literal such as "".
You may want to use your loop to determine the offsets of the start of the word in your string that you're looking for. Then find its length by continuing through the string until you encounter the end or another space. Then, you can malloc an array of chars with size equal to the size of the offset+1 (For the null terminator.) Finally, copy the substring into this new buffer and return it.
Also, as mentioned above, you may want to remove the strlen call from the loop - most compilers will optimize it out but it is indeed a linear operation for every character in the array, making the loop O(n**2).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *arg(const char *S, unsigned int Num) {
char *Return = "";
const char *top, *p;
unsigned int Spaces = 0;
int i = 0;
Return=(char*)malloc(sizeof(char));
*Return = '\0';
if(S == NULL || *S=='\0') return Return;
p=top=S;
while(Spaces != Num){
if(NULL!=(p=strchr(top, ' '))){
++Spaces;
top=++p;
} else {
break;
}
}
if(Spaces < Num) return Return;
if(NULL!=(p=strchr(top, ' '))){
int len = p - top;
Return=(char*)realloc(Return, sizeof(char)*(len+1));
strncpy(Return, top, len);
Return[len]='\0';
} else {
free(Return);
Return=strdup(top);
}
//printf("%s-\n", Return);
return Return;
}
int main(){
char *word;
word=arg("make a quick function", 2);//quick
printf("\"%s\"\n", word);
free(word);
return 0;
}

Resources