I am a beginner in C programming and I built a function that recieves the string "HodHasharon,frozenYogurt,100". The function cuts the string into 3 pieces where every piece is a field of my City struct.
I want to put "HodHasharon" into
City pcity.name (name of the city), "frozenYogurt" into pcity.popularFood (popular food) and number of residents (100) into pcity.residents.
When i am debugging the output in my function the output is correct but when i print from the main.c I got a concatenated string.
For example, when I print pcity.name I get "HodHashafrozenYod" instead of "HodHasharon" but if I do printf at my function printf->name I get the correct output of "HodHasharon"
What am I doing wrong?
struct of City:
typedef struct City
{
char *name;
char * popluarFood;
int numberOfPeople;
} City;
the function:
City * cutCityData (char *singleLine)
{
City* pcity=(City*)malloc(sizeof(City));
int firstIndex=1;
int endIndex=1;
int checkItarion=0;
while(endIndex<strlen(singleLine))
{//while
while (singleLine[endIndex] != ',')
{//while2
endIndex++;
}//while2
checkItarion++;
char cityDetails[endIndex - firstIndex +1];
memcpy(cityDetails,&singleLine[firstIndex], endIndex);
cityDetails[endIndex - firstIndex] = '\0';
if (checkItarion == 1) {
pcity->name = (char *) malloc(cityDetails);
strcpy(&(pcity->name), cityDetails);
endIndex++;
firstIndex = endIndex;
}
if (checkItarion == 2) {
pcity->popluarFood = (char *) malloc(cityDetails);
strcpy(&(pcity->popluarFood), cityDetails);
endIndex++;
firstIndex=endIndex;
break;
}
}//while
char cityDetails[strlen(singleLine) - firstIndex + 1];
memcpy(cityDetails, &singleLine[firstIndex], sizeof(singleLine-1));
int resdints=atoi(cityDetails);
pcity->numberOfPeople=resdints;
return pcity;
}
from main:
City* pCity=cutCityData(singLine);
printf("%s\n", &(pCity->name));
&(pcity->name) is the address of the pointer variable. You want to copy the string to the memory that it points to, not copy over the pointer. So change:
strcpy(&(pcity->name), cityDetails);
to
strcpy(pcity->name, cityDetails);
You're also giving the wrong argument to malloc(). cityDetails is an array, but the argument should be the number of bytes you want to allocate. So change
pcity->name = (char *) malloc(cityDetails);
to:
pcity->name = malloc(strlen(cityDetails) + 1);
These changes also need to be made for the code that fills in pcity->popularFood as well.
This is wrong:
memcpy(cityDetails, &singleLine[firstIndex], sizeof(singleLine-1));
singleLine is a pointer, so sizeof(singleLine-1) is the number of bytes in a pointer, not the length of the string. This should be:
memcpy(cityDetails, &singleLine[firstIndex], endIndex + 1);
Related
i want to replace _ (underscore) with white spaces and make the first letter of the name and the surname to upper case while printing the nameList in searchKeyword method.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void searchKeyword(const char * nameList[], int n, const char keyword[])
{
int i,name=0;
char *str;
const char s[2] = " " ;
for(i=0;i<n;i++)
{
char *str = (char *) malloc((strlen(nameList[0])+1)*sizeof(char));
strcpy(str,nameList[i]);
strtok(str,"_");
if(strcmp(keyword,strtok(NULL,"_"))==0) // argument NULL will start string
{ // from last point of previous string
name++;
if(nameList[i] == '_')
strcpy(nameList[i],s);
//nameList[i] = ' ';
printf("%s\n",nameList[i]);
}
}
if(name==0)
{
printf("No such keyword found\n");
}
free(str); //deallocating space
}
int main()
{
char p1[] = "zoe_bale";
char p2[] = "sam_rodriguez";
char p3[] = "jack_alonso";
char p4[] = "david_studi";
char p5[] = "denzel_feldman";
char p6[] = "james_bale";
char p7[] = "james_willis";
char p8[] = "michael_james";
char p9[] = "dustin_bale";
const char * nameList[9] = {p1, p2, p3, p4, p5, p6, p7, p8, p9};
char keyword[100];
printf("Enter a keyword: ");
scanf("%s", keyword);
printf("\n");
searchKeyword(nameList, 9, keyword);
printf("\n");
for (int i = 0; i < 9; i++)
printf("%s\n",nameList[i]);
return 0;
}
Search through the strings and print the ones whose surname part is equal to keyword.
As shown in the example runs below, the strings are printed in “Name Surname” format (the first letters are capitalized).
Output should be like this:
Enter a keyword: james
Michael James
zoe_bale
sam_rodriguez
jack_alonso
david_studi
denzel_feldman
james_bale
james_willis
michael_james
dustin_bale
There is no reason to dynamically allocate storage for your name and surname. Looking at your input, neither will exceed 9-characters, so simply using an array for each of 64-chars provides 6X the storage required (if you are unsure, double that to 128-chars and have 1200% additional space). That avoids the comparatively expensive calls to malloc.
To check whether keyword exists in nameList[i], you don't need to separate the values first and then compare. Simply use strstr (nameList[i], keyword) to determine if keyword is contained in nameList[i]. If you then want to match only the name or surname you can compare again after they are separated. (up to you)
To parse the names from the nameList[i] string, all you need is a single pointer to locate the '_' character. A simple call to strchr() will do and it does not modify nameList[i] so there is no need to duplicate.
After using strchr() to locate the '_' character, simply memcpy() from the start of nameList[i] to your pointer to your name array, increment the pointer and then strcpy() from p to surname. Now you have separated name and surname, simply call toupper() on the first character of each and then output the names separate by a space, e.g.
...
#include <ctype.h>
#define NLEN 64
void searchKeyword (const char *nameList[], int n, const char keyword[])
{
for (int i = 0; i < n; i++) { /* loop over each name in list */
if (strstr (nameList[i], keyword)) { /* does name contain keyword? */
char name[NLEN], surname[NLEN]; /* storage for name, surname */
const char *p = nameList[i]; /* pointer to parse nameList[i] */
if ((p = strchr(p, '_'))) { /* find '_' in nameList[i] */
/* copy first-name to name */
memcpy (name, nameList[i], p - nameList[i]);
name[p++ - nameList[i]] = 0; /* nul-terminate first name */
*name = toupper (*name); /* convert 1st char to uppwer */
/* copy last name to surname */
strcpy (surname, p);
*surname = toupper (*surname); /* convert 1st char to upper */
printf ("%s %s\n", name, surname); /* output "Name Surname" */
}
}
}
}
Example Use/Output
Used with the remainder of your code, searching for "james" locates those names containing "james" and provides what looks like the output you requested, e.g.
$ ./bin/keyword_surname
Enter a keyword: james
James Bale
James Willis
Michael James
zoe_bale
sam_rodriguez
jack_alonso
david_studi
denzel_feldman
james_bale
james_willis
michael_james
dustin_bale
(note: to match only the name or surname add an additional strcmp before the call to printf to determine which you want to output)
Notes On Your Existing Code
Additional notes continuing from the comments on your existing code,
char *str = (char *) malloc((strlen(nameList[0])+1)*sizeof(char));
should simply be
str = malloc (strlen (nameList[i]) + 1);
You have previously declared char *str; so the declaration before your call to malloc() shadows your previous declaration. If you are using gcc/clang, you can add -Wshadow to your compile string to ensure you are warned of shadowed variables. (they can have dire consequences in other circumstances)
Next, sizeof (char) is always 1 and should be omitted from your size calculation. There is no need to cast the return of malloc() in C. See: Do I cast the result of malloc?
Your comparison if (nameList[i] == '_') is a comparison between a pointer and integer and will not work. Your compiler should be issuing a diagnostic telling you that is incorrect (do not ignore compiler warnings -- do not accept code until it compiles without warning)
Look things over and let me know if you have further questions.
that worked for me and has no memory leaks.
void searchKeyword(const char * nameList[], int n, const char keyword[])
{
int found = 0;
const char delim = '_';
for (int i = 0; i < n; i++) {
const char *fst = nameList[i];
for (const char *tmp = fst; *tmp != '\0'; tmp++) {
if (*tmp == delim) {
const char *snd = tmp + 1;
int fst_length = (snd - fst) / sizeof(char) - 1;
int snd_length = strlen(fst) - fst_length - 1;
if (strncmp(fst, keyword, fst_length) == 0 ||
strncmp(snd, keyword, snd_length) == 0) {
found = 1;
printf("%c%.*s %c%s\n",
fst[0]-32, fst_length-1, fst+1,
snd[0]-32, snd+1);
}
break;
}
}
}
if (!found)
puts("No such keyword found");
}
hopefully it's fine for you too, although I use string.h-functions very rarely.
I'm certain that I'm using pointer syntax incorrectly inside of my insertTowns function because when I try to run my file I just get a segmentation fault when I uncomment the lines involving pointers in that function. I know that I have the function set up right logically just not syntactically. What am I doing wrong in my pointer syntax for insertTowns?
To keep things simple, assume there is nothing wrong with FILE * infile and the arrays have been malloc'd correctly in a separate file. Also, length is initialized to 0 and then passed into the readFile function from main in another .c file. So it's not an array out of bounds problem.
I've watched numerous youtube videos on pointers (thenewboston had some good videos), looked at http://cslibrary.stanford.edu/106/ and a few other resources.
I've provide a snippet of my code below rather than the whole program to keep it simple since it's just a syntax problem:
typedef struct cityStruct { unsigned int zip; char * town; } city;
typedef struct zipTownsStruct {
int * zips; // indexs to main array cities sorted by zip
city * * towns; // pointers to main array cities sorted by town name
city * cities; // main array of cities in order from file not sorted
} zipTowns;
extern void insertTowns(zipTowns arrs, int * length) {
int j = (*length) - 1;
while (j >= 0 && ((strcmp(arrs.towns[j]->town, arrs.cities[*length].town)) > 0)) {
*arrs.towns[j + 1] = *arrs.towns[j];
j--;
}
*arrs.towns[j + 1] = arrs.cities[*length];
}
extern void readFile(zipTowns arrs, FILE * infile, int * length) {
char * zipCode;
char * town;
if((zipCode = malloc(sizeof(char) * 6)) == NULL) {
fprintf(stderr, "%s\n", strerror(errno));
exit(errno);
}
if((town = malloc(sizeof(char) * 26)) == NULL) {
fprintf(stderr, "%s\n", strerror(errno));
exit(errno);
}
while(fscanf(infile,"%s %s", zipCode, town) == 2) {
arrs.cities[*length].zip = atoi(zipCode);
arrs.cities[*length].town = town;
insertZips(arrs, length);
insertTowns(arrs, length);
(*length)++;
}
free(zipCode);
free(town);
}
in
extern void insertTowns(zipTowns arrs, int * length) {
int j = (*length) - 1;
while (j >= 0 && ((strcmp(arrs.towns[j]->town, arrs.cities[*length].town)) > 0)) {
*arrs.towns[j + 1] = *arrs.towns[j];
j--;
}
*arrs.towns[j + 1] = arrs.cities[*length];
}
if length is the number of entries of arrs.towns you start with j = (*length) - 1 so j+1 == *length and *arrs.towns[j + 1] access out of the array with undefined behavior. Probably the same in arrs.cities[*length], it seems also strange to copy always the same city.
In an array of length elements the valid indexes are 0 .. length-1
Warning
zipCode = malloc(sizeof(char) * 5)
allows to store zipcode of 4 characters max to have the place for the ending null character (in France zipcode uses 5 characters, may be not the case for you, but you do not give enough information allowing us to know)
It is difficult to say more because you do not give Minimal, Complete, and Verifiable example
I got the following string from the user:
char *abc = "a234bc567d";
but all the numbers can have different lengths than in this example (letters are constants).
How can I get each part of numbers? (again, it can be 234 or 23743 or something else..)
I tried to use strchr and strncpy but I need to allocate memory for this (for strncpy), and I hope there is a better solution.
Thanks.
You can do something like this:
char *abc = "a234bc567d";
char *ptr = abc; // point to start of abc
// While not at the end of the string
while (*ptr != '\0')
{
// If position is the start of a number
if (isdigit(*ptr))
{
// Get value (assuming base 10), store end position of number in ptr
int value = strtol(ptr, &ptr, 10);
printf("Found value %d\n", value);
}
else
{
ptr++; // Increase pointer
}
}
If I understand your question, you are trying to extract the parts of the user input that contain numbers ... and the sequence of numbers can be variable ... but the letters are fixed i.e. a or b or c or d. Correct ... ? The following program may help you. I tried it for strings "a234bc567d", "a23743bc567d" and "a23743bc5672344d". Works ...
int main()
{
char *sUser = "a234bc567d";
//char *sUser = "a23743bc567d";
//char *sUser = "a23743bc5672344d";
int iLen = strlen(sUser);
char *sInput = (char *)malloc((iLen+1) * sizeof(char));
strcpy(sInput, sUser);
char *sSeparator = "abcd";
char *pToken = strtok(sInput, sSeparator);
while(1)
{
if(pToken == NULL)
break;
printf("Token = %s\n", pToken);
pToken = strtok(NULL, sSeparator);
}
return 0;
}
Why If I printf myarray[x] in main function I get no data (a blank line)?
Array is correctly filled (if I print in function I get values)
Here is my code:
int main(void) {
char thisxpath[300];
char thisurl[200];
char** myarray = NULL;
strcpy (thisurl,"http://api.openweathermap.org/data/2.5/weather?q=Pescara&mode=xml&units=metric");
strcpy (thisxpath,"//city/#name | //country | //weather/#value | //temperature/#value | //precipitation/#value | //humidity/#value | //speed/#*[name()='name' or name()='value']");
xmlretrive (thisurl, thisxpath, &myarray);
printf("%s\n", myarray[1]);
free(myarray);
return 0;
}
void xmlretrive(char* myurl, char* myxpath, char** myarray) {
//code that retrieve with cURL the XML and other stuff
//keyword contain data, that are copied into myarray
myarray = malloc(20 * sizeof(char*));
for (i=0; i < nodeset->nodeNr; i++) {
keyword = xmlNodeListGetString(doc, nodeset->nodeTab[i]->xmlChildrenNode, 1);
myarray[i] = malloc((100) * sizeof(char));
strcpy(myarray[i], keyword);
// if I printf("%s\n", myarray[i]) here I can see that array is actually filled
xmlFree(keyword);
}
You're passing a copy of myarray to xmlretrive. If you want to change what myarray points to inside xmlretrive, you need to pass a pointer to it. i.e. a char***
void xmlretrive(char* myurl, char* myxpath, char*** myarray) {
*myarray = malloc(20 * sizeof(char*));
for (i=0; i < nodeset->nodeNr; i++) {
keyword = xmlNodeListGetString(doc, nodeset->nodeTab[i]->xmlChildrenNode, 1);
(*myarray)[i] = malloc(strlen(keyword)+1);
if ((*myarray)[i] == NULL) {
// out of memory. print error msg then exit
}
strcpy((*myarray)[i], keyword);
xmlFree(keyword);
}
Note that I've also suggested some changes to your malloc line
you shouldn't cast the return from malloc
allocate the exact length of string needed for keyword to avoid the possibility of buffer overflows for strlen(keyword)>99
sizeof(char) is guaranteed to be 1 so you don't need to multiply the allocation size by it
This will solve your immediate problem but might not be enough to get things working properly. Some other things to consider:
main needs to call free for each allocated member of myarray as well as myarray itself
you don't have any way for main to know the length of myarray. You could either pass a separate length argument into xmlretrive or change xmlretrive to add a NULL element at the end of the array and iterate until you find this in main
xmlretrive should probably allocate space for nodeset->nodeNr + 1 (+1 assumes you add a NULL terminator to the array) elements rather than hard-coding a length of 20
My function is being passed a struct containing, among other things, a NULL terminated array of pointers to words making up a command with arguments.
I'm performing a glob match on the list of arguments, to expand them into a full list of files, then I want to replace the passed argument array with the new expanded one.
The globbing is working fine, that is, g.gl_pathv is populated with the list of expected files. However, I am having trouble copying this array into the struct I was given.
#include <glob.h>
struct command {
char **argv;
// other fields...
}
void myFunction( struct command * cmd )
{
char **p = cmd->argv;
char* program = *p++; // save the program name (e.g 'ls', and increment to the first argument
glob_t g;
memset(&g, 0, sizeof(g));
g.gl_offs = 1;
int res = glob(*p++, GLOB_DOOFFS, NULL, &g);
glob_handle_res(res);
while (*p)
{
res = glob(*p, GLOB_DOOFFS | GLOB_APPEND, NULL, &g);
glob_handle_res(res);
}
if( g.gl_pathc <= 0 )
{
globfree(&g);
}
cmd->argv = malloc((g.gl_pathc + g.gl_offs) * sizeof *cmd->argv);
if (cmd->argv == NULL) { sys_fatal_error("pattern_expand: malloc failed\n");}
// copy over the arguments
size_t i = g.gl_offs;
for (; i < g.gl_pathc + g.gl_offs; ++i)
cmd->argv[i] = strdup(g.gl_pathv[i]);
// insert the original program name
cmd->argv[0] = strdup(program);
** cmd->argv[g.gl_pathc + g.gl_offs] = 0; **
globfree(&g);
}
void
command_free(struct esh_command * cmd)
{
char ** p = cmd->argv;
while (*p) {
free(*p++); // Segfaults here, was it already freed?
}
free(cmd->argv);
free(cmd);
}
Edit 1: Also, I realized I need to stick program back in there as cmd->argv[0]
Edit 2: Added call to calloc
Edit 3: Edit mem management with tips from Alok
Edit 4: More tips from alok
Edit 5: Almost working.. the app segfaults when freeing the command struct
Finally: Seems like I was missing the terminating NULL, so adding the line:
cmd->argv[g.gl_pathc + g.gl_offs] = 0;
seemed to make it work.
argv is an array of pointers of char *. This means that argv has space for argc char * values. If you try to copy more than that many char * values into it, you will end up with an overflow.
Most likely your glob call results in more than argc elements in gl_pathv field (i.e, gl_pathc > argc). This is undefined behavior.
It is similar to the code below:
/* Wrong code */
#include <string.h>
int a[] = { 1, 2, 3 };
int b[] = { 1, 2, 3, 4 };
memcpy(a, b, sizeof b);
Solution: you should either work with the glob_t struct directly, or allocate new space to copy gl_pathv to a new char **:
char **paths = malloc(g.gl_pathc * sizeof *paths);
if (paths == NULL) { /* handle error */ }
for (size_t i=0; i < g.gl_pathc; ++i) {
/* The following just copies the pointer */
paths[i] = g.gl_pathv[i];
/* If you actually want to copy the string, then
you need to malloc again here.
Something like:
paths[i] = malloc(strlen(g.gl_pathv[i] + 1));
followed by strcpy.
*/
}
/* free all the allocated data when done */
Edit: after your edit:
cmd->argv = calloc(g.gl_pathc, sizeof(char *) *g.gl_pathc);
it should work, but each of argv[1] to argv[g.gl_pathc + g.gl_offs - 1] is a char * that is "owned" by the struct glob. Your memcpy call is only copying the pointers. When you later do globfree(), those pointers don't mean anything anymore. So, you need to do copy the strings for your use:
size_t i;
cmd->argv = malloc((g.gl_pathc+g.gl_offs) * sizeof *cmd->argv);
for (i=g.gl_offs; i < g.gl_pathc + g.gl_offs; ++i)
cmd->argv[i] = strdup(g.gl_pathv[i]);
This makes sure you now have your own private copies of the strings. Be sure to free them (and argv) once you are done.
There are a few other problems with your code.
You are doing *p++, you should do p++, since you're not using the value of the dereferencing.
You should really check the return value of glob.
Your paths variable needs g.gl_pathc + 1 elements, not g.gl_pathc. (Or more correctly, you need to allocate g.gl_pathc + g.gl_offs times sizeof *paths bytes.)
Your for loop to copy strings should be for (j=1; j < g.gl_pathc + g.gl_offs; ++j).
Make sure you prevent shell from expanding your glob. I.e., call ./a.out '*' instead of ./a.out *.
Don't you need to multiple g.gl_pathc by sizeof(char *)?