C pointer mistakes in my understanding of the problem - c

The output result after execution is______
#include<stdio.h>
int main()
{ char str[]="xyz",*ps=str;
while(*ps)
ps++;
for(ps--;ps-str>=0;ps--)
puts(ps);
return 0;
}
The right answer is
z
yz
xyz
My logic is
After while(*ps) ps++; *psrepresents " " the last element of the array
When execute codefor(ps--;ps-str>=0;ps--)puts(ps);,*ps go back two positions everytime.
So I think t should be
z
xyz

Probably closer to what you want:
int main()
{
char str[]="xyz";
size_t length = sizeof(str)-1;
// size_t length = strlen(str); // safer version of above.
for (size_t i = 0; i < length; i++)
{
char* ps = str+length-i-1;
puts(ps);
}
}

"After while(*ps) ps++; *ps represents " ""
No, *ps is then '\0'.
When execute code for(ps--;ps-str>=0;ps--)puts(ps);, *ps go back two positions everytime
No, it does not. It starts by going back one step, then in each iteration it only goes back one step. It's the same as doing this:
{
ps--; // for(ps--; ...; ...)
while(ps-str >= 0) { // for(...; ps-str >= 0; ...)
puts(ps);
ps--; // for(...; ...; ps--)
}
}
ps-str>=0 is not a good test though. If you step ps "behind" the start of str the comparison is invalid.
A safe version could simply be:
#include <stdio.h>
int main() {
char str[] = "xyz", *ps = str;
while (*ps) ps++;
while (ps != str) {
--ps;
puts(ps);
}
}

Related

In C, how do I remove identical, and consecutive lines in a char array?

I'm looking for some help in creating a function.
The funciton, deleteIdents(), will remove identical lines in a char array, given they are consecutive. It will keep one of the identical lines.
I don't need to check if the whole line is identical. Just the first 79 chars, MAXCHARS, will be fine for this scenario.
So, for example, if my array contains
Hello World
Hi World
Hello World
Hello World
Hello World
Hi there
it will be changed to
Hello World
Hi World
Hello World
Hi there
In my head, the function would look similar to:
int deleteIdents(char *a)
{
int i;
for (i=0; i<=MAXCHARS; i++) {
if (a[i] != '\n')
/* copy into new array */
}
}
}
but I'm unsure. If you have a solution, I'd be glad and thankful to hear it :)
Well read first line than second, compare them if they are equal go into the loop until they are not equal. So here is code:
char *first_line = malloc(MAXLINE);
char *second_line = malloc(MAXLINE);
getline(first_line);
do {
getline(second_line);
} while (strcmp (first_line, second_line));
For getline() implementation search SO there are plenty of examples. Or here you have mine.
Another example how it can be achived. Idea is to keep 2 pointers, and increment first one only if entries are different. Also some additional storage is allocated to avoid memory leaks for entries that has been overriden.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int unique(char **strings, int size) {
if (!strings) {
return -1;
}
int head = 0, newHead = 0, duplicatedElementsHead = 0;
//Save duplicates to avoid memory leaks
char** duplicatedEntries = malloc(size*sizeof(char*));
while (head < size) {
//String are the same
if (!strcmp(strings[head], strings[newHead])) {
if (head != newHead) {
duplicatedEntries[duplicatedElementsHead++] = strings[newHead];
}
++head;
} else {
strings[++newHead] = strings[head++];
}
}
//Put duplicated entries after new end
int idx = 0, tmpHead = newHead + 1;
for (; idx < duplicatedElementsHead; ++idx) {
strings[tmpHead++] = duplicatedEntries[idx];
}
free(duplicatedEntries);
return newHead;
}
int main() {
char **strings = malloc(8*sizeof(char*));
strings[0] = "Hello World";
strings[1] = "Hi World";
strings[2] = "Hi World";
strings[3] = "Hello World";
strings[4] = "Hello World";
strings[5] = "Hi there";
strings[6] = "Hia";
strings[7] = "Hi";
int newEnd = unique(strings, 8);
for (int i=0; i < newEnd; ++i) {
printf("%s\n", strings[i]);
}
free(strings);
}
You are essentially writing the core functionality of the unix/linux utility, 'uniq'.
cat filename | sort | uniq > newfile
#or skip sort, since you didn't mention
cat filename | uniq > newfile
You could just use popen and uniq (something like this...)
FILE *uniqfh;
uniqfh = popen("cat file1 | uniq" , "r");
if (uniqfh == NULL) { //handle error
}
while( fgets(uniqfh, buffer, buffersize) ) printf("%s\n",buffer);
But seriously, you could write the core of uniq(),
static long MAXUNIQ=79; //or whatever you want
char*
isdup(char* prev, char* next, long len)
{
//if( !prev || !next) error
long n = len<=0 ? MAXUNIQ : len;
for( ; *prev==*next && n --> 0; ) { //down-to operator (sic)
; //clearly nothing happening here!
}
return( (n<1) || !(*p+*n) );
}
/yeah, this is actually strncmp, but hey
You need an array of 'strings' (char* or char[]), let's read them,
char* ray[ARRAYMAX]; //define how many elements of your arRay
//could use, char** ray; and malloc(ARRAYMAX*sizeof(char*))
long
read_array(FILE* fh, char* ray[])
{
char buffer[MAXLINE+1];
long count=0;
while( fgets(buffer,sizeof(buffer),fh) ) {
//you could eat dups here, or in separate function below
//if( (count<1) && !isdup(ray[count-1],buffer,MAXUNIQ) )
ray[count++] = strdup(buffer);
}
//ray[0] through ray[count-1] contain char*
//count contains number of strings read
return count;
}
long
deleteIdents(long raysize, char* ray[]) //de-duplicate
{
long kept, ndx;
for( ndx=1, kept=0; ndx<raysize; ++ndx ) {
if( !isdup(ray[kept],ray[ndx]) ) {
ray[kept++] = ray[ndx];
}
else {
free(ray[ndx]);
ray[ndx] = NULL; //not entirely necessary,
}
}
return kept; //new ray size
}
And you will need to to this to call it...
...
long raysize;
char* ray[ARRAYMAX] = {0}; //init to null pointers
raysize = read_array(fopen(filename,"r"),ray);
raysize = deleteIndents(raysize,ray);
...
Later, you will need to free the malloc'ed strings,
for( ; 0 <-- raysize; ) { free(ray[raysize]); ray[raysize] = NULL; }
The following program does what you need on an array of string elements. We navigate the array with two pointers, initialised to the first and second elements. We run a loop of array_n - 1 comparisons of one element to the next one, comparing both strings... if they are different, we copy the *source_ptr string pointer to the *target_ptr place. If they are different we only increment the source_ptr, so it points to the next array string, but without copying it (this makes us to effectively delete the pointer) We are also managing the new array (we have used the same array as source and destination, as we can only delete array elements, so each time we have a greater hole between both pointers)
pru.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* array of strings */
char *array[] = {
"Hello World",
"Hi World",
"Hello World",
"Hello World",
"Hello World",
"Hi there",
};
size_t array_n = sizeof array / sizeof *array;
int main()
{
int i;
char **target_ptr = array, **source_ptr = array + 1;
size_t new_length = 1;
for (i = 1; i < array_n; i++) {
/* if strings pointed to by pointers are equal */
if (strcmp(*target_ptr, *source_ptr) == 0) {
/* go to the next, effectively discarding the second pointer */
source_ptr++;
} else {
/* copy both pointers in place, to the destination array */
*target_ptr++ = *source_ptr++;
new_length++; /* increment array length */
}
}
/* finally, we have in the array only the good pointers */
/* print'em */
for (i = 0; i < new_length; i++)
printf("%s\n", array[i]);
exit(0);
}
and that's all.
sample run:
$ pru
Hi World
Hello World
Hi there
Hello World
$ _

Getting incorrect values when accessing variables passed along in a pointer to a character array for strtok

Here is my code
//Split up the config by lines
int x;
int numberOfConfigLines = 0;
for (x = 0; x < strlen(buffer); x++)
{
if (buffer[x] == '\n') {
numberOfConfigLines++;
}
}
char *configLines[numberOfConfigLines];
tokenize(configLines, buffer, "\n", numberOfConfigLines);
The idea of this function is to count the amount of newlines in a buffer, then split the buffer into a strtok array using this:
#include <string.h>
#include <stdlib.h>
void tokenize(char **arrToStoreTokens, char *delimitedString, char *delimiter, int expectedTokenArraySize) {
//Create a clone of the original string to prevent making permanent changes to it
char *tempString = (char *)malloc(strlen(delimitedString) + 1);
strcpy(tempString, delimitedString);
if (expectedTokenArraySize >= 1) {
arrToStoreTokens[0] = strtok(tempString, delimiter);
int x;
for (x = 1; x < expectedTokenArraySize; x++ ) {
arrToStoreTokens[x] = strtok(NULL, delimiter);
}
}
//Dispose of temporary clone
free(tempString);
}
If I access arrToStoreTokens[0] directly, I get the correct result, however when I try to access configLines[0] once thetokenize function has ended, I get different results (can be unknown characters or simply empty)
Additionally, I believe this has only started occurring once I began running the program as root (for a different requirement) - I may be wrong though. - EDIT: Confirmed not to be the problem.
Any ideas?
strtok doesn't reallocate anything. It only makes cut and pointers of what you gave to it.
Your array stores pointers that strtok gives you, but don't copy contents.
So if you free your tempString variable, you free data that was pointed by return values of strtok. You have to keep it and free it only at the end.
Or you can make a strdup of each return of strtok to store it in your array to make a real copy of each token, but in this case, you shall have to free each token at the end.
The second solution would looks like this :
void tokenize(char **arrToStoreTokens, char *delimitedString, char *delimiter, int expectedTokenArraySize) {
//Create a clone of the original string to prevent making permanent changes to it
char *tempString = (char *)malloc(strlen(delimitedString) + 1);
strcpy(tempString, delimitedString);
if (expectedTokenArraySize >= 1) {
arrToStoreTokens[0] = strdup(strtok(tempString, delimiter)); // Here is the new part : strdup
int x;
for (x = 1; x < expectedTokenArraySize; x++ ) {
arrToStoreTokens[x] = strdup(strtok(NULL, delimiter)); // Here is the new part : strdup
}
}
//Dispose of temporary clone
free(tempString);
}
And after use of this array, you shall have to delete it, with a function like this :
void deleteTokens(char **arrToStoreTokens, int arraySize)
{
int x;
for (x = 0; x < arraySize; ++x)
{
free(arrToStoreTokens[x]);
}
}

How to concatenate two strings in c language

I have the below code which concatenates two strings. The first one with while condition works, but the second one with for loop fails. I ran them independently with the below inputs.
int original[100]="c" , add[50] = "pgm";
concatenate_string(original, add);
printf("String after concatenation is \"%s\"\n", original);
//strcat(original,add);
//printf("strcat is : %s",original);
void concatenate_string(char *original, char *add)
{
while(*original)
original++;
while(*add)
{
*original = *add;
add++;
original++;
}
*original = '\0';
}
void strcat(char *original,char *add)
{
for (;*original++;);
for (;*add;)
*original++=*add++;
*(original)='\0';
}
Because in second code, you are appending string after nul \0 symbol:
for (;*original++;);
^ always increments, even after nul found
should be:
for (;*original; original++);
Note:
for code:
for (;*original++;);
is equivalent to
while (*original++);
But not what you are using.
Your second version does an extra ++ before the second loop starts. Try adding --original; between the two loops, or as the initialization in the second loop.
#include<stdio.h>
#include<string.h>
void string_add(char *original,char *add) {
int i = 0,j,length = 0;
i = strlen(original);
length = strlen(add);
for(j = 0; j<length; j++,i++)
original[i] = add[j];
}
int main()
{
char original[20] = "I am from ", add[10] = "india";
string_add(original, add);
printf("Concatinated : %s\n",original);
return 0;
}

Resetting a char buffer in C

I'm working on a homework assignment and I need to basically create a character buffer. One of the functions I need to create is called "b_reset". It's purpose is to reinitialize the given buffer so that it will point to the first position in the char buffer. This is needed because later on, when a new char is added to the buffer, it needs to be added to the first position in the buffer.
This is the code I have thus far:
The struct:
typedef struct BufferDescriptor {
char * ca_head ;
int capacity ;
char inc_factor;
int addc_offset ;
int mark_offset ;
char r_flag;
char mode;
} Buffer ;
The code:
int b_reset ( Buffer *pB )
{
Buffer *temp = NULL;
int i = 0;
int j = 1;
if (pB == NULL)
{
return R_FAIL_1;
}
else
{
temp = (Buffer*)malloc(sizeof(Buffer*));
if (temp == NULL)
{
return R_FAIL_1;
}
temp->ca_head = (char*)malloc(pB->capacity);
if (!temp->ca_head)
{
temp = NULL;
return R_FAIL_1;
}
for(i = 0;i < ca_getsize(pB);++i)
{
temp->ca_head[j] = pB->ca_head[i];
j++;
}
pB->ca_head = temp->ca_head;
//free(temp->ca_head);
//free(temp);
return 0;
}
}
My goal in this code was to create a temporary buffer that would basically shift over everything 1 time based on the actual given buffer. This would make the first position empty so another char could be added.
The problem I'm running into is that the original buffer doesn't seem to be returning the right values after I reset it.
When I do this for example:
temp->ca_head[0] = 'a';
temp->ca_head[1] = 'b';
temp->ca_head[2] = 'c';
temp->ca_head[3] = 'd';
temp->ca_head[4] = 'e';
b_reset(temp); //this will return the size as 0, when it's actually 5
//temp->ca_head[0] = 'i'; //if this is executed, it returns the size as 6
//and prints out the right values, but if it's not,
//it will not print out anything
printf("%d", ca_getsize(temp));
for(i = 0;i < ca_getsize(temp);++i)
{
printf("%c", temp->ca_head[i]);
}
I know something is going wrong here, but I'm not too sure what. Any suggestions would be greatly appreciated.
This code is based on your followup comment:
well I'm not trying to resize the buffer, I just want to create an
empty space in the first position, so basically shifting everything to
the right 1 time. The assumption is that there is a enough space in
the buffer to handle this process.
I don't think you need to do any malloc() ing beyond the initial one. You can just shift everything up in a loop:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define R_FAIL_1 1
#define BUFFER_SIZE 10
typedef struct BufferDescriptor {
char * ca_head ;
int capacity ;
char inc_factor;
int addc_offset ;
int mark_offset ;
char r_flag;
char mode;
} Buffer ;
void allocate_buffer(Buffer *pB, int size)
{
pB->ca_head = malloc(size);
assert(pB->ca_head);
pB->capacity = size;
}
int ca_getsize( Buffer *pB)
{
return pB->capacity;
}
int b_reset ( Buffer *pB )
{
int i = 0;
if (pB == NULL)
{
return R_FAIL_1;
}
else
{
if ( ca_getsize(pB) <= 0 || pB->ca_head == NULL )
return R_FAIL_1;
}
// shift data up by 1 byte
for( i = ca_getsize(pB) - 1 ; i > 0;i-- )
{
pB->ca_head[i] = pB->ca_head[i-1];
}
pB->ca_head[0] = '\0';
return 0;
}
void print_buffer(Buffer *pB)
{
printf("capacity: %d \n", ca_getsize(pB));
for (int i = 0;i < ca_getsize(pB);++i)
{
printf("buffer(%d): [%d] ",i, pB->ca_head[i]);
}
printf("\n");
}
int main(void)
{
Buffer a_buffer;
allocate_buffer(&a_buffer,BUFFER_SIZE);
strcpy(a_buffer.ca_head,"abcdefgh");
print_buffer(&a_buffer);
int ret = b_reset(&a_buffer);
assert(ret == 0);
print_buffer(&a_buffer);
}
temp = (Buffer*)malloc(sizeof(Buffer*));
You need to allocate enough space to hold a Buffer, but you only allocate enough space to hold a pointer to a buffer. This should be:
temp = (Buffer*)malloc(sizeof(Buffer));
You are managing your memory incorrectly. You are allocating memory for a new Buffer struct when actually you only need to handle the memory of the ca_head member (if my interpretation of your homework problem is correct).
Each time you invoke b_reset, you will allocate memory for this struct that will not be released. If you don't handle your memory correctly, you will experience unexpected results as the one you are reporting in your question.
I suggest you to make a research on the function realloc and use it properly in your b_reset function.
Good luck with your homework.

splitting a full filename into parts

I am creating a function that will split a full unix filename(like /home/earlz/test.bin) into its individual parts. I have got a function, and it works for the first two parts perfect, but after that it produces erroneous output...
strlcpy_char will copy a string using term as the terminator, as well as 0.
If it is terminated with term, then term will be the last character of the string, then null.
returns trg string length...
int strlcpy_char(char *trg,const char *src,int max,char term){
int i;
if(max==0){return 0;}
for(i=0;i<max-1;i++){
if(*src==0){
*trg=0;
return i;
}
if(*src==term){
*trg=term;
trg++;
*trg=0; //null terminate
return i+1;
}
*trg=*src;
src++;
trg++;
}
*trg=0;
return max;
}
.
int get_path_part(char *file,int n,char *buf){
int i;
int current_i=0;
//file is assumed to start with '/'so it skips the first character.
for(i=0;i<=n;i++){
current_i++;
current_i=strlcpy_char(buf,&file[current_i],MAX_PATH_PART_SIZE,'/');
if(current_i<=1){ //zero length string..
kputs("!"); //just a debug message. This never happens with the example
return -1; //not enough parts to the path
}
}
if(buf[current_i-1]=='/'){
return 1; //is not the last part
}else{
return 0; //is the last part(the file part)
}
}
I use this code to test it:
kputs("test path: ");
kgets(cmd);
kputs("\n");
char *tmp=malloc(256);
int i=0;
get_path_part(cmd,i,tmp);
kputs(tmp);
kputs("\n");
i=1;
get_path_part(cmd,i,tmp);
kputs(tmp);
kputs("\n");
i=2;
get_path_part(cmd,i,tmp);
kputs(tmp);
kputs("\n");
When I try something like "/home/test.bin" it works right outputting
/home
/test.bin
But when I try "/home/earlz/test.bin" I get
/home
/earlz
/arlz
Anyone see the problem in my code, as I've been looking but I just can't see any problem.
Also, before you say "but there is a library for that" I am doing this in an operating system kernel, so I barely have a standard library. I only have parts of string.h and really that's about it for standard.
You overwrite current_i instead of adding it up as you walk through the path.
So
current_i++;
current_i=strlcpy_char(buf,&file[current_i],MAX_PATH_PART_SIZE,'/');
should really be
current_i += strlcpy_char(buf,&file[current_i+1],MAX_PATH_PART_SIZE,'/');
I think you need to track your current_i for i>1 since the max value returned from the strlcpy has no idea of where you are in the overall file string. does it make sense?
current_i=strlcpy_char(buf,&file[current_i],MAX_PATH_PART_SIZE,'/');
Don't you need to do something like
tocurrent_i += strlcpy_char...
instead of
tocurrent_i = strlcpy_char...
Does your code have to be re-entrant?
If not use strtok, it is in strings.h
STRTOK(P)
NAME
strtok, strtok_r - split string into tokens
SYNOPSIS
#include <string.h>
char *strtok(char *restrict s1, const char *restrict s2);
char *strtok_r(char *restrict s, const char *restrict sep,
char **restrict lasts);
Sorry for not commenting on your code though :)
If you are using Glib, g_strsplit is very nice and easy to use.
This is how I'd do it
char ** split_into_parts(char *path) {
char ** parts = malloc(sizeof(char *) * 100);
int i = 0;
int j = 0;
if (*path == '/') {
path++;
}
parts[0] = 0;
while (*path) {
if (*path == '/') {
parts[i][j] = 0;
i++;
parts[i] = 0;
j = 0;
} else {
if (parts[i] == 0) {
parts[i] = malloc(sizeof(char) * 100);
}
parts[i][j] = *path;
j++;
}
path++;
}
parts[i+1] = 0;
return parts;
}
Try something like the code I have below.
If you need implementations of standard C functions (like strchr()) try koders.com or just google for strchr.c.
#include <stdio.h>
#include <string.h>
const char *NextToken(const char *pStart, char chSep, char *pToken, size_t nTokMax)
{
const char *pEnd;
size_t nLength;
/* set output to empty */
*pToken=0;
/* make sure input is OK */
if (!pStart || *pStart!=chSep)
return NULL;
/* find end of token */
pEnd = strchr(pStart+1, chSep);
if (pEnd)
nLength = pEnd - pStart;
else
nLength = strlen(pStart);
if (nLength >= nTokMax) /* too big */
return NULL;
strncpy(pToken, pStart, nLength);
pToken[nLength] = 0;
return pEnd;
}
int main()
{
#define BUFFSIZE 256
char cmd[BUFFSIZE];
char tmp[BUFFSIZE];
const char *pStart=cmd;
int i=0;
puts("test path: ");
fgets(cmd, BUFFSIZE, stdin);
puts("");
do {
pStart = NextToken(pStart, '/', tmp, BUFFSIZE);
if (tmp[0])
puts(tmp);
} while (pStart);
return 0;
}

Resources